diff --git a/.github/ISSUE_TEMPLATE/hacktoberfest-issue.md b/.github/ISSUE_TEMPLATE/hacktoberfest-issue.md deleted file mode 100644 index 9a30df81c..000000000 --- a/.github/ISSUE_TEMPLATE/hacktoberfest-issue.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: Hacktoberfest Issue -about: "- DO NOT USE - Please post your suggestions on discord and we will create - an issue for you if applicable." -title: '' -labels: Hacktoberfest -assignees: '' - ---- - - - - - - -This Issue is part of [Hacktoberfest](https://hacktoberfest.digitalocean.com/) - A yearly event which encourages participation in the open-source community. [Sign up on their website](https://hacktoberfest.digitalocean.com/) and submit **four pull requests during october (Oct 1st - Oct 31st)** to any public open-source project on GitHub and earn a limited-edition T-shirt or plant a tree! - -## :mag_right: Scope -The following bullet points explain what the scope of this feature should be. -It should give a general idea of what we want, you can of course deviate from this as needed. - - - -## :anchor: Difficulty -Here is our honest estimate on how difficult (on a scale of 1-5) the implementation may be: - - -:white_circle::white_circle::white_circle::white_circle::white_circle: - -## :construction: Technical Challenges -These are some challenges which may need to be overcome, we wanna be as transparent as possible, so here is some guidance on what may present itself as an obstacle. - - - -## :memo: Relevant Classes or Snippets -Here are some classes or code snippets which we think might help you get a better understanding where to look for in this gigantic codebase - - - -## :book: Useful Resources -If you need help on how to get started, maybe try looking into the following resources! - -* Hacktoberfest - * [Getting started with Hacktoberfest](https://hacktoberfest.digitalocean.com/details#get-started) - * [Hacktoberfest FAQ](https://hacktoberfest.digitalocean.com/faq) -* GitHub/Open-Source - * [How to contribute to Open-Source](https://opensource.guide/how-to-contribute/) - * [Working with forks](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/working-with-forks) - * [Creating a Pull Request](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) -* Slimefun - * [Contributing to Slimefun](https://github.com/Slimefun/Slimefun4/blob/master/CONTRIBUTING.md) - * [Code of Conduct](https://github.com/Slimefun/Slimefun4/blob/master/.github/CODE_OF_CONDUCT.md) - -
- -If you want to work on this, simply comment down below and state your interests! Please also comment again if you have changed your mind. Anyone is allowed to discuss in the comments below, feel free to collaborate and work together :heart:
-You can always ask for help here if you get stuck. diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml deleted file mode 100644 index d9c720087..000000000 --- a/.github/workflows/update-changelog.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Changelog Populator - -on: - push: - branches: - - master - paths: - - 'CHANGELOG.md' - -jobs: - populate: - - name: Changelog Generator - runs-on: ubuntu-latest - if: github.repository == 'Slimefun/Slimefun4' - - steps: - - name: TOC Generator - uses: technote-space/toc-generator@v2.6.1 - with: - GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} - TARGET_PATHS: 'CHANGELOG.md' - TOC_TITLE: '**Table of contents**' - COMMIT_MESSAGE: '[CI skip] Updated Changelog' - COMMIT_NAME: 'TheBusyBot' - CREATE_PR: false - MAX_HEADER_LEVEL: 2 - FOLDING: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e0473ab3..dc9c496d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ **Table of contents** -- [Release Candidate 18 (TBD)](#release-candidate-18-tbd) +- [Release Candidate 19 (TBD)](#release-candidate-19-tbd) +- [Release Candidate 18 (03 Dec 2020)](#release-candidate-18-03-dec-2020) - [Release Candidate 17 (17 Oct 2020)](#release-candidate-17-17-oct-2020) - [Release Candidate 16 (07 Sep 2020)](#release-candidate-16-07-sep-2020) - [Release Candidate 15 (01 Aug 2020)](#release-candidate-15-01-aug-2020) @@ -23,13 +24,24 @@ -## Release Candidate 18 (TBD) +## Release Candidate 19 (TBD) + +#### Changes +* Performance optimizations for Cargo networks +* Removed an old version of bStats + +## Release Candidate 18 (03 Dec 2020) #### Additions * The Smelters Pick now also works on Ancient Debris * (API) Added PlayerPreResearchEvent * Added a config option to disable network visualizations * (API) Added CoolerFeedPlayerEvent +* Added a config option to delete excess cargo network items +* Added an item setting to configure the Wind Staff velocity +* Added an item setting to the Infused Hopper to toggle it with redstone +* Added an item setting to prevent Reinforced Spawners from being changed by Spawn Eggs +* Added 4 bricks -> 1 brick block recipe to the Electric Press #### Changes * Removed 1.13 support @@ -40,6 +52,12 @@ * Magnets can no longer be placed down * Electromagnets can no longer be placed down * Performance improvements to Cargo network visualizations +* General performance improvements +* Improved performance for radioactive items +* Memory/GC improvements for the profiler +* Performance improvements for the Fluid Pump +* Removed EmeraldEnchants integration +* Memory and performance improvements for ticking blocks #### Fixes * Fixed #2448 @@ -59,6 +77,23 @@ * Fixed Magician Talisman sometimes drawing invalid enchantments * Fixed id conflicts for external Enchantment sources (e.g. plugins) for the Magician Talisman settings * Fixed network visualizers spawning particles for other player heads +* Fixed #2418 +* Fixed #2446 +* Fixed CoreProtect not recognizing Slimefun blocks getting broken +* Fixed #2447 +* Fixed #2558 +* Fixed a duplication bug with the Block Placer +* Fixed Slimefun Guide Settings showing "last activity" as a negative number +* Fixed Armor Stands getting damaged/pushed by Explosive Bow +* Fixed Sword of Beheading dropping Zombie/Skeleton Skulls from Zombie/Skeleton subvariants +* Fixed #2518 +* Fixed #2421 +* Fixed #2574 +* Fixed color in android script downloading screen +* Fixed #2576 +* Fixed #2496 +* Fixed #2585 +* Fixed #2583 ## Release Candidate 17 (17 Oct 2020) diff --git a/README.md b/README.md index a80303a5e..182532689 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Here is a full summary of the differences between the two different versions of | | development (latest) | "stable" | | ------------------ | -------- | -------- | -| **Minecraft version(s)** | :video_game: **1.14.\* - 1.16.\*** | :video_game: **1.13.\* - 1.16.\*** | +| **Minecraft version(s)** | :video_game: **1.14.\* - 1.16.\*** | :video_game: **1.14.\* - 1.16.\*** | | **automatic updates** | :heavy_check_mark: | :heavy_check_mark: | | **frequent updates** | :heavy_check_mark: | :x: | | **latest content** | :heavy_check_mark: | :x: | diff --git a/pom.xml b/pom.xml index 1d4321fc8..66e575bae 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,6 @@ slimefun https://sonarcloud.io DEBUG - src/main/java/me/mrCookieSlime/Slimefun/bstats/bukkit/Metrics.java target/site/jacoco/jacoco.xml @@ -315,7 +314,7 @@ com.github.seeseemelk MockBukkit-v1.16 - 0.15.0 + 0.16.0 test @@ -329,7 +328,7 @@ org.mockito mockito-core - 3.6.0 + 3.6.28 test @@ -337,13 +336,13 @@ com.github.TheBusyBiscuit CS-CoreLib2 - 0.26 + 0.27.4 compile io.papermc paperlib - 1.0.5 + 1.0.6 compile @@ -381,7 +380,7 @@ com.gmail.nossr50.mcMMO mcMMO - 2.1.154 + 2.1.159 provided @@ -414,12 +413,6 @@ - - com.github.TheBusyBiscuit - EmeraldEnchants2 - 3cd370b5d8 - provided - me.minebuilders clearlag-core diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java index 3f49247a9..33a057f07 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java @@ -1,6 +1,7 @@ package io.github.thebusybiscuit.slimefun4.api; import org.apache.commons.lang.Validate; +import org.bukkit.Server; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; @@ -36,21 +37,45 @@ public enum MinecraftVersion { * This constant represents an exceptional state in which we were unable * to identify the Minecraft Version we are using */ - UNKNOWN("Unknown"), + UNKNOWN("Unknown", true), /** * This is a very special state that represents the environment being a Unit * Test and not an actual running Minecraft Server. */ - UNIT_TEST("Unit Test Environment"); + UNIT_TEST("Unit Test Environment", true); public static final MinecraftVersion[] valuesCache = values(); private final String name; + private final boolean virtual; private final String prefix; + /** + * This constructs a new {@link MinecraftVersion} with the given name. + * This constructor forces the {@link MinecraftVersion} to be real. + * It must be a real version of Minecraft. + * + * @param name + * The display name of this {@link MinecraftVersion} + */ MinecraftVersion(String name) { + this(name, false); + } + + /** + * This constructs a new {@link MinecraftVersion} with the given name. + * A virtual {@link MinecraftVersion} (unknown or unit test) is not an actual + * version of Minecraft but rather a state of the {@link Server} software. + * + * @param name + * The display name of this {@link MinecraftVersion} + * @param virtual + * Whether this {@link MinecraftVersion} is virtual + */ + MinecraftVersion(String name, boolean virtual) { this.name = name; + this.virtual = virtual; this.prefix = name().replace("MINECRAFT_", "v") + '_'; } @@ -63,6 +88,19 @@ public enum MinecraftVersion { return name; } + /** + * This returns whether this {@link MinecraftVersion} is virtual or not. + * A virtual {@link MinecraftVersion} does not actually exist but is rather + * a state of the {@link Server} software used. + * Virtual {@link MinecraftVersion MinecraftVersions} include "UNKNOWN" and + * "UNIT TEST". + * + * @return Whether this {@link MinecraftVersion} is virtual or not + */ + public boolean isVirtual() { + return virtual; + } + /** * This method checks whether the given version matches with this * {@link MinecraftVersion}. diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/PlayerPreResearchEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/PlayerPreResearchEvent.java index 12282bda7..d80efd9b1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/PlayerPreResearchEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/PlayerPreResearchEvent.java @@ -33,7 +33,7 @@ public class PlayerPreResearchEvent extends Event implements Cancellable { private final Research research; private final SlimefunItem slimefunItem; private boolean cancelled; - + @ParametersAreNonnullByDefault public PlayerPreResearchEvent(Player p, Research research, SlimefunItem slimefunItem) { Validate.notNull(p, "The Player cannot be null"); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/PlayerRightClickEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/PlayerRightClickEvent.java index 1bfd42977..4d09ebbed 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/PlayerRightClickEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/PlayerRightClickEvent.java @@ -11,6 +11,7 @@ import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; @@ -19,12 +20,25 @@ import io.github.thebusybiscuit.cscorelib2.data.ComputedOptional; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.BlockStorage; -public class PlayerRightClickEvent extends Event { +/** + * The {@link PlayerRightClickEvent} is our custom version of the {@link PlayerInteractEvent}. + * But it is only triggered on right click. + * The main and (almost) sole purpose of this {@link Event} is to cache the {@link SlimefunItem} + * of the {@link ItemStack} and/or {@link Block} involved. + * This allows us (and addons) to efficiently check the used {@link SlimefunItem} without the need + * to do a heavy lookup or item comparison. + * + * @author TheBusyBiscuit + * + */ +public class PlayerRightClickEvent extends PlayerEvent { private static final HandlerList handlers = new HandlerList(); + /** + * The original {@link PlayerInteractEvent}. + */ private final PlayerInteractEvent event; - private final Player player; private final Optional itemStack; private final Optional clickedBlock; @@ -35,33 +49,45 @@ public class PlayerRightClickEvent extends Event { private ComputedOptional slimefunItem = ComputedOptional.createNew(); private ComputedOptional slimefunBlock = ComputedOptional.createNew(); - private Result itemResult = Result.DEFAULT; - private Result blockResult = Result.DEFAULT; + private Result itemResult; + private Result blockResult; - public PlayerRightClickEvent(@Nonnull PlayerInteractEvent e) { - event = e; - player = e.getPlayer(); - clickedBlock = Optional.ofNullable(e.getClickedBlock()); - face = e.getBlockFace(); - hand = e.getHand(); + /** + * This constructs a new {@link PlayerRightClickEvent} based on the original {@link PlayerInteractEvent}. + * The {@link Result} of the original {@link PlayerInteractEvent} will be copied. + * + * @param originalEvent + * The original {@link PlayerInteractEvent} + */ + public PlayerRightClickEvent(@Nonnull PlayerInteractEvent originalEvent) { + super(originalEvent.getPlayer()); - if (e.getItem() == null || e.getItem().getType() == Material.AIR || e.getItem().getAmount() == 0) { + event = originalEvent; + clickedBlock = Optional.ofNullable(originalEvent.getClickedBlock()); + face = originalEvent.getBlockFace(); + hand = originalEvent.getHand(); + + itemResult = originalEvent.useItemInHand(); + blockResult = originalEvent.useInteractedBlock(); + + if (originalEvent.getItem() == null || originalEvent.getItem().getType() == Material.AIR || originalEvent.getItem().getAmount() == 0) { itemStack = Optional.empty(); } else { - itemStack = Optional.of(e.getItem()); + itemStack = Optional.of(originalEvent.getItem()); } } + /** + * This returns the original {@link PlayerInteractEvent} that triggered this + * {@link PlayerRightClickEvent}. + * + * @return The original {@link PlayerInteractEvent} + */ @Nonnull public PlayerInteractEvent getInteractEvent() { return event; } - @Nonnull - public Player getPlayer() { - return player; - } - /** * This method returns the {@link ItemStack} that was held in the hand of the {@link Player}. * It will never return null, should there be no {@link ItemStack} then it will return @@ -121,6 +147,10 @@ public class PlayerRightClickEvent extends Event { return slimefunBlock.getAsOptional(); } + /** + * This method cancels the {@link PlayerRightClickEvent}. + * This will deny the item and block usage. + */ public void cancel() { itemResult = Result.DENY; blockResult = Result.DENY; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSetting.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSetting.java index e51ff0bcb..8d0a27c14 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSetting.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemSetting.java @@ -113,6 +113,7 @@ public class ItemSetting { * * @param c * The class of data type you want to compare + * * @return Whether this {@link ItemSetting} stores the given type */ public boolean isType(@Nonnull Class c) { @@ -137,14 +138,19 @@ public class ItemSetting { * @param item * The {@link SlimefunItem} who called this method */ - @SuppressWarnings("unchecked") public void load(@Nonnull SlimefunItem item) { + Validate.notNull(item, "Cannot apply settings for a non-existing SlimefunItem"); + SlimefunPlugin.getItemCfg().setDefaultValue(item.getId() + '.' + getKey(), getDefaultValue()); Object configuredValue = SlimefunPlugin.getItemCfg().getValue(item.getId() + '.' + getKey()); if (defaultValue.getClass().isInstance(configuredValue)) { - if (validateInput((T) configuredValue)) { - this.value = (T) configuredValue; + // We can suppress the warning here, we did an isInstance(...) check before! + @SuppressWarnings("unchecked") + T newValue = (T) configuredValue; + + if (validateInput(newValue)) { + this.value = newValue; } else { Slimefun.getLogger().log(Level.WARNING, "Slimefun has found an invalid config setting in your Items.yml!"); Slimefun.getLogger().log(Level.WARNING, " at \"{0}.{1}\"", new Object[] { item.getId(), getKey() }); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java index e4d06619e..9f026b2e5 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java @@ -132,10 +132,15 @@ public abstract class Network { * * @param l * The {@link Location} to check for + * * @return Whether the given {@link Location} is part of this {@link Network} */ public boolean connectsTo(@Nonnull Location l) { - return connectedLocations.contains(l); + if (regulator.equals(l)) { + return true; + } else { + return connectedLocations.contains(l); + } } @Nullable 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 538a1dc49..e3af4f416 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java @@ -31,7 +31,6 @@ import io.github.thebusybiscuit.slimefun4.core.researching.Research; import io.github.thebusybiscuit.slimefun4.implementation.guide.BookSlimefunGuide; import io.github.thebusybiscuit.slimefun4.implementation.guide.CheatSheetSlimefunGuide; import io.github.thebusybiscuit.slimefun4.implementation.guide.ChestSlimefunGuide; -import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutomatedCraftingChamber; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; @@ -85,8 +84,6 @@ public final class SlimefunRegistry { private final Map, Set> globalItemHandlers = new HashMap<>(); private final Map blockHandlers = new HashMap<>(); - private final Map automatedCraftingChamberRecipes = new HashMap<>(); - public void load(@Nonnull Config cfg) { Validate.notNull(cfg, "The Config cannot be null!"); @@ -261,18 +258,6 @@ public final class SlimefunRegistry { return geoResources; } - /** - * This method returns a list of recipes for the {@link AutomatedCraftingChamber} - * - * @deprecated This just a really bad way to do this. Someone needs to rewrite this. - * - * @return A list of recipes for the {@link AutomatedCraftingChamber} - */ - @Deprecated - public Map getAutomatedCraftingChamberRecipes() { - return automatedCraftingChamberRecipes; - } - public boolean logDuplicateBlockEntries() { return logDuplicateBlockEntries; } 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 3cd3241d8..f630e5e00 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 @@ -49,7 +49,9 @@ public interface DamageableItem extends ItemAttribute { */ default void damageItem(@Nonnull Player p, @Nullable ItemStack item) { if (isDamageable() && item != null && item.getType() != Material.AIR && item.getAmount() > 0) { - if (item.getEnchantments().containsKey(Enchantment.DURABILITY) && Math.random() * 100 <= (60 + Math.floorDiv(40, (item.getEnchantmentLevel(Enchantment.DURABILITY) + 1)))) { + int unbreakingLevel = item.getEnchantmentLevel(Enchantment.DURABILITY); + + if (unbreakingLevel > 0 && Math.random() * 100 <= (60 + Math.floorDiv(40, (unbreakingLevel + 1)))) { return; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/EnergyNetComponent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/EnergyNetComponent.java index 6d2e1504d..62c1246fa 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/EnergyNetComponent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/EnergyNetComponent.java @@ -10,6 +10,7 @@ import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponen import io.github.thebusybiscuit.slimefun4.implementation.items.electric.Capacitor; import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; +import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.BlockStorage; @@ -65,14 +66,36 @@ public interface EnergyNetComponent extends ItemAttribute { * @return The charge stored at that {@link Location} */ default int getCharge(@Nonnull Location l) { + // Emergency fallback, this cannot hold a charge, so we'll just return zero + if (!isChargeable()) { + return 0; + } + + return getCharge(l, BlockStorage.getLocationInfo(l)); + } + + /** + * This returns the currently stored charge at a given {@link Location}. + * This is a more performance saving option if you already have a {@link Config} + * object for this {@link Location}. + * + * @param l + * The target {@link Location} + * @param data + * The data at this {@link Location} + * + * @return The charge stored at that {@link Location} + */ + default int getCharge(@Nonnull Location l, @Nonnull Config data) { Validate.notNull(l, "Location was null!"); + Validate.notNull(data, "data was null!"); // Emergency fallback, this cannot hold a charge, so we'll just return zero if (!isChargeable()) { return 0; } - String charge = BlockStorage.getLocationInfo(l, "energy-charge"); + String charge = data.getString("energy-charge"); if (charge != null) { return Integer.parseInt(charge); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/ProtectionType.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/ProtectionType.java index bb2fd0a17..ac0e488c7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/ProtectionType.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/ProtectionType.java @@ -7,6 +7,7 @@ import org.bukkit.entity.Bee; * prevents the damage from. * * @author Linox + * @author Seggan * * @see ProtectiveArmor * diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/FlexCategory.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/FlexCategory.java index 8f6494a84..28a05cfa9 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/FlexCategory.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/FlexCategory.java @@ -68,8 +68,11 @@ public abstract class FlexCategory extends Category { @Override public final boolean isHidden(@Nonnull Player p) { - // We can stop this method right here. - // We provide a custom method with more parameters for this. See isVisible(...) + /** + * We can stop this method right here. + * We provide a custom method with more parameters for this. + * See isVisible(...) + */ return false; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/LockedCategory.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/LockedCategory.java index 97a529e8b..69b2434cd 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/LockedCategory.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/LockedCategory.java @@ -153,9 +153,11 @@ public class LockedCategory extends Category { public boolean hasUnlocked(@Nonnull Player p, @Nonnull PlayerProfile profile) { for (Category category : parents) { for (SlimefunItem item : category.getItems()) { - // Should probably be replaced with Slimefun.hasUnlocked(...) - // However this will result in better performance because we don't - // request the PlayerProfile everytime + /** + * Should probably be replaced with Slimefun.hasUnlocked(...) + * However this will result in better performance because we don't + * request the PlayerProfile everytime + */ if (Slimefun.isEnabled(p, item, false) && Slimefun.hasPermission(p, item, false) && !profile.hasUnlocked(item.getResearch())) { return false; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/SeasonalCategory.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/SeasonalCategory.java index 538b0aaed..4e0920d18 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/SeasonalCategory.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/SeasonalCategory.java @@ -27,7 +27,7 @@ public class SeasonalCategory extends Category { private final Month month; /** - * The constructor for a SeasonCategory. + * The constructor for a {@link SeasonalCategory}. * * @param key * The {@link NamespacedKey} that is used to identify this {@link Category} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunCommand.java index 5bbc09820..04b1347d6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunCommand.java @@ -83,9 +83,12 @@ public class SlimefunCommand implements CommandExecutor, Listener { sendHelp(sender); - // We could just return true here, but if there's no subcommands, then - // something went horribly wrong anyway. This will also stop sonarcloud - // from nagging about this always returning true... + /** + * We could just return true here, but if there's no subcommands, + * then something went horribly wrong anyway. + * This will also stop sonarcloud from nagging about + * this always returning true... + */ return !commands.isEmpty(); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SubCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SubCommand.java index 28b54fd18..3bc2630bf 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SubCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SubCommand.java @@ -77,6 +77,7 @@ public abstract class SubCommand { * * @param sender * The {@link CommandSender} who requested the description + * * @return A possibly localized description of this {@link SubCommand} */ @Nonnull diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/ContributorsMenu.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/ContributorsMenu.java index 5ed9c6918..7ce154874 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/ContributorsMenu.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/ContributorsMenu.java @@ -46,7 +46,7 @@ final class ContributorsMenu { }); List contributors = new ArrayList<>(SlimefunPlugin.getGitHubService().getContributors().values()); - contributors.sort(Comparator.comparingInt(Contributor::index)); + contributors.sort(Comparator.comparingInt(Contributor::getPosition)); for (int i = page * 36; i < contributors.size() && i < (page + 1) * 36; i++) { Contributor contributor = contributors.get(i); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/RainbowTickHandler.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/RainbowTickHandler.java index bcd5f0ac8..78fe5c98f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/RainbowTickHandler.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/handlers/RainbowTickHandler.java @@ -75,9 +75,11 @@ public class RainbowTickHandler extends BlockTicker { } for (Material type : materials) { - // This BlockData is purely virtual and only created on startup, it should have - // no impact on performance, in fact it should save performance as it preloads - // the data but also saves heavy calls for other Materials + /** + * This BlockData is purely virtual and only created on startup, it should have + * no impact on performance, in fact it should save performance as it preloads + * the data but also saves heavy calls for other Materials + */ if (type.createBlockData() instanceof GlassPane) { return true; } @@ -89,8 +91,10 @@ public class RainbowTickHandler extends BlockTicker { @Override public void tick(Block b, SlimefunItem item, Config data) { if (b.getType() == Material.AIR) { - // The block was broken, setting the Material now would result in a - // duplication glitch + /** + * The block was broken, setting the Material now would result in a + * duplication glitch + */ return; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlockMachine.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlockMachine.java index 54f4d6f74..a0f7f33e7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlockMachine.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/multiblocks/MultiBlockMachine.java @@ -117,7 +117,7 @@ public abstract class MultiBlockMachine extends SlimefunItem implements NotPlace protected MultiBlockInteractionHandler getInteractionHandler() { return (p, mb, b) -> { if (mb.equals(getMultiBlock())) { - if (!isDisabled() && SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES) && Slimefun.hasUnlocked(p, this, true)) { + if (!isDisabled() && SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK) && Slimefun.hasUnlocked(p, this, true)) { onInteract(p, b); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java index a7c4089a8..66216750d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java @@ -14,6 +14,7 @@ import org.bukkit.Server; import io.github.thebusybiscuit.cscorelib2.config.Config; import io.github.thebusybiscuit.slimefun4.api.network.Network; +import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet; import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener; /** @@ -30,6 +31,7 @@ public class NetworkManager { private final int maxNodes; private final boolean enableVisualizer; + private final boolean deleteExcessItems; private final List networks = new LinkedList<>(); /** @@ -37,14 +39,29 @@ public class NetworkManager { * * @param maxStepSize * The maximum amount of nodes a {@link Network} can have + * @param enableVisualizer + * Whether the {@link Network} visualizer is enabled + * @param deleteExcessItems + * Whether excess items from a {@link CargoNet} should be voided */ - public NetworkManager(int maxStepSize, boolean enableVisualizer) { + public NetworkManager(int maxStepSize, boolean enableVisualizer, boolean deleteExcessItems) { Validate.isTrue(maxStepSize > 0, "The maximal Network size must be above zero!"); this.enableVisualizer = enableVisualizer; + this.deleteExcessItems = deleteExcessItems; maxNodes = maxStepSize; } + /** + * This creates a new {@link NetworkManager} with the given capacity. + * + * @param maxStepSize + * The maximum amount of nodes a {@link Network} can have + */ + public NetworkManager(int maxStepSize) { + this(maxStepSize, true, false); + } + /** * This method returns the limit of nodes a {@link Network} can have. * This value is read from the {@link Config} file. @@ -64,6 +81,16 @@ public class NetworkManager { return enableVisualizer; } + /** + * This returns whether excess items from a {@link CargoNet} should be voided + * instead of being dropped to the ground. + * + * @return Whether to delete excess items + */ + public boolean isItemDeletionEnabled() { + return deleteExcessItems; + } + /** * This returns a {@link List} of every {@link Network} on the {@link Server}. * @@ -81,6 +108,7 @@ public class NetworkManager { } Validate.notNull(type, "Type must not be null"); + for (Network network : networks) { if (type.isInstance(network) && network.connectsTo(l)) { return Optional.of(type.cast(network)); @@ -141,8 +169,11 @@ public class NetworkManager { public void updateAllNetworks(@Nonnull Location l) { Validate.notNull(l, "The Location cannot be null"); - for (Network network : getNetworksFromLocation(l, Network.class)) { - network.markDirty(l); + // No need to create a sublist and loop through it if there are no Networks + if (!networks.isEmpty()) { + for (Network network : getNetworksFromLocation(l, Network.class)) { + network.markDirty(l); + } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/AbstractItemNetwork.java similarity index 87% rename from src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/AbstractItemNetwork.java index 4af214e38..a7abb9eae 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ChestTerminalNetwork.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/AbstractItemNetwork.java @@ -23,6 +23,7 @@ import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; @@ -47,12 +48,13 @@ import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu; import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; /** - * An abstract super class of {@link CargoNet} that handles interactions with ChestTerminal. + * An abstract super class of {@link CargoNet} that handles + * interactions with ChestTerminal. * * @author TheBusyBiscuit * */ -abstract class ChestTerminalNetwork extends Network { +abstract class AbstractItemNetwork extends Network { private static final int[] slots = { 19, 20, 21, 28, 29, 30, 37, 38, 39 }; private static final int[] TERMINAL_SLOTS = { 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 18, 19, 20, 21, 22, 23, 24, 27, 28, 29, 30, 31, 32, 33, 36, 37, 38, 39, 40, 41, 42 }; @@ -64,14 +66,23 @@ abstract class ChestTerminalNetwork extends Network { protected final Set imports = new HashSet<>(); protected final Set exports = new HashSet<>(); - // This represents a Queue of requests to handle + /** + * This represents a {@link Queue} of requests to handle + */ private final Queue itemRequests = new LinkedList<>(); - // This is a cache for the BlockFace a node is facing, so we don't need to request the - // BlockData each time we visit a node + /** + * This is a cache for the {@link BlockFace} a node is facing, so we don't need to + * request the {@link BlockData} each time we visit a node + */ protected Map connectorCache = new HashMap<>(); - protected ChestTerminalNetwork(Location regulator) { + /** + * This is our cache for the {@link ItemFilter} for each node. + */ + protected Map filterCache = new HashMap<>(); + + protected AbstractItemNetwork(Location regulator) { super(SlimefunPlugin.getNetworkManager(), regulator); } @@ -127,7 +138,7 @@ abstract class ChestTerminalNetwork extends Network { Optional target = getAttachedBlock(l); if (target.isPresent()) { - item = CargoUtils.insert(inventories, l.getBlock(), target.get(), item); + item = CargoUtils.insert(this, inventories, l.getBlock(), target.get(), item); if (item == null) { terminal.replaceExistingItem(request.getSlot(), null); @@ -159,7 +170,7 @@ abstract class ChestTerminalNetwork extends Network { Optional target = getAttachedBlock(l); if (target.isPresent()) { - ItemStack is = CargoUtils.withdraw(inventories, l.getBlock(), target.get(), item); + ItemStack is = CargoUtils.withdraw(this, inventories, l.getBlock(), target.get(), item); if (is != null) { if (stack == null) { @@ -201,7 +212,7 @@ abstract class ChestTerminalNetwork extends Network { Optional target = getAttachedBlock(bus); if (target.isPresent()) { - ItemStackAndInteger stack = CargoUtils.withdraw(inventories, bus.getBlock(), target.get()); + ItemStackAndInteger stack = CargoUtils.withdraw(this, inventories, bus.getBlock(), target.get()); if (stack != null) { menu.replaceExistingItem(17, stack.getItem()); @@ -227,7 +238,7 @@ abstract class ChestTerminalNetwork extends Network { if (menu.getItemInSlot(17) != null) { Optional target = getAttachedBlock(bus); - target.ifPresent(block -> menu.replaceExistingItem(17, CargoUtils.insert(inventories, bus.getBlock(), block, menu.getItemInSlot(17)))); + target.ifPresent(block -> menu.replaceExistingItem(17, CargoUtils.insert(this, inventories, bus.getBlock(), block, menu.getItemInSlot(17)))); } if (menu.getItemInSlot(17) == null) { @@ -274,7 +285,7 @@ abstract class ChestTerminalNetwork extends Network { * found in any provider of the network. * * @param providers - * A {@link Set} of providers to this {@link ChestTerminalNetwork} + * A {@link Set} of providers to this {@link AbstractItemNetwork} * * @return The time it took to compute this operation */ @@ -326,10 +337,27 @@ abstract class ChestTerminalNetwork extends Network { @Override public void markDirty(@Nonnull Location l) { - connectorCache.remove(l); + markCargoNodeConfigurationDirty(l); super.markDirty(l); } + /** + * This will mark the {@link ItemFilter} of the given node dirty. + * It will also invalidate the cached rotation. + * + * @param node + * The {@link Location} of the cargo node + */ + public void markCargoNodeConfigurationDirty(@Nonnull Location node) { + ItemFilter filter = filterCache.get(node); + + if (filter != null) { + filter.markDirty(); + } + + connectorCache.remove(node); + } + @ParametersAreNonnullByDefault private void updateTerminal(Location l, BlockMenu terminal, int slot, int index, List items) { if (items.size() > index) { @@ -430,20 +458,20 @@ abstract class ChestTerminalNetwork extends Network { int stored = Integer.parseInt(data); for (int slot : blockMenu.getPreset().getSlotsAccessedByItemTransport((DirtyChestMenu) blockMenu, ItemTransportFlow.WITHDRAW, null)) { - ItemStack is = blockMenu.getItemInSlot(slot); + ItemStack stack = blockMenu.getItemInSlot(slot); - if (is != null && CargoUtils.matchesFilter(l.getBlock(), is)) { + if (stack != null && CargoUtils.matchesFilter(this, l.getBlock(), stack)) { boolean add = true; for (ItemStackAndInteger item : items) { - if (SlimefunUtils.isItemSimilar(is, item.getItemStackWrapper(), true, false)) { + if (SlimefunUtils.isItemSimilar(stack, item.getItemStackWrapper(), true, false)) { add = false; - item.add(is.getAmount() + stored); + item.add(stack.getAmount() + stored); } } if (add) { - items.add(new ItemStackAndInteger(is, is.getAmount() + stored)); + items.add(new ItemStackAndInteger(stack, stack.getAmount() + stored)); } } } @@ -461,7 +489,7 @@ abstract class ChestTerminalNetwork extends Network { @ParametersAreNonnullByDefault private void filter(@Nullable ItemStack stack, List items, Location node) { - if (stack != null && CargoUtils.matchesFilter(node.getBlock(), stack)) { + if (stack != null && CargoUtils.matchesFilter(this, node.getBlock(), stack)) { boolean add = true; for (ItemStackAndInteger item : items) { @@ -477,4 +505,21 @@ abstract class ChestTerminalNetwork extends Network { } } + @Nonnull + protected ItemFilter getItemFilter(@Nonnull Block node) { + Location loc = node.getLocation(); + ItemFilter filter = filterCache.get(loc); + + if (filter == null) { + ItemFilter newFilter = new ItemFilter(node); + filterCache.put(loc, newFilter); + return newFilter; + } else if (filter.isDirty()) { + filter.update(node); + return filter; + } else { + return filter; + } + } + } 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 f5592a493..34b83e260 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 @@ -24,7 +24,7 @@ import me.mrCookieSlime.Slimefun.api.Slimefun; /** * The {@link CargoNet} is a type of {@link Network} which deals with {@link ItemStack} transportation. - * It is also an extension of {@link ChestTerminalNetwork} which provides methods to deal + * It is also an extension of {@link AbstractItemNetwork} which provides methods to deal * with the addon ChestTerminal. * * @author meiamsome @@ -37,7 +37,7 @@ import me.mrCookieSlime.Slimefun.api.Slimefun; * @author DNx5 * */ -public class CargoNet extends ChestTerminalNetwork { +public class CargoNet extends AbstractItemNetwork { private static final int RANGE = 5; private static final int TICK_DELAY = SlimefunPlugin.getCfg().getInt("networks.cargo-ticker-delay"); 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 051b964d2..cff4bb867 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 @@ -15,6 +15,7 @@ import org.bukkit.block.Block; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; @@ -32,11 +33,12 @@ import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu; * * @see CargoNet * @see CargoUtils - * @see ChestTerminalNetwork + * @see AbstractItemNetwork * */ class CargoNetworkTask implements Runnable { + private final NetworkManager manager; private final CargoNet network; private final Map inventories = new HashMap<>(); @@ -49,6 +51,7 @@ class CargoNetworkTask implements Runnable { @ParametersAreNonnullByDefault CargoNetworkTask(CargoNet network, Map inputs, Map> outputs, Set chestTerminalInputs, Set chestTerminalOutputs) { this.network = network; + this.manager = SlimefunPlugin.getNetworkManager(); this.inputs = inputs; this.outputs = outputs; @@ -65,8 +68,10 @@ class CargoNetworkTask implements Runnable { network.handleItemRequests(inventories, chestTerminalInputs, chestTerminalOutputs); } - // All operations happen here: Everything gets iterated from the Input Nodes. - // (Apart from ChestTerminal Buses) + /** + * All operations happen here: Everything gets iterated from the Input Nodes. + * (Apart from ChestTerminal Buses) + */ SlimefunItem inputNode = SlimefunItems.CARGO_INPUT_NODE.getItem(); for (Map.Entry entry : inputs.entrySet()) { long nodeTimestamp = System.nanoTime(); @@ -89,8 +94,9 @@ class CargoNetworkTask implements Runnable { SlimefunPlugin.getProfiler().closeEntry(network.getRegulator(), SlimefunItems.CARGO_MANAGER.getItem(), timestamp); } + @ParametersAreNonnullByDefault private void routeItems(Location inputNode, Block inputTarget, int frequency, Map> outputNodes) { - ItemStackAndInteger slot = CargoUtils.withdraw(inventories, inputNode.getBlock(), inputTarget); + ItemStackAndInteger slot = CargoUtils.withdraw(network, inventories, inputNode.getBlock(), inputTarget); if (slot == null) { return; @@ -105,30 +111,35 @@ class CargoNetworkTask implements Runnable { } if (stack != null) { - Inventory inv = inventories.get(inputTarget.getLocation()); + insertItem(inputTarget, previousSlot, stack); + } + } - if (inv != null) { - // Check if the original slot hasn't been occupied in the meantime - if (inv.getItem(previousSlot) == null) { - inv.setItem(previousSlot, stack); - } else { - // Try to add the item into another available slot then - ItemStack rest = inv.addItem(stack).get(0); + @ParametersAreNonnullByDefault + private void insertItem(Block inputTarget, int previousSlot, ItemStack item) { + Inventory inv = inventories.get(inputTarget.getLocation()); - if (rest != null) { - // If the item still couldn't be inserted, simply drop it on the ground - inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), rest); - } - } + if (inv != null) { + // Check if the original slot hasn't been occupied in the meantime + if (inv.getItem(previousSlot) == null) { + inv.setItem(previousSlot, item); } else { - DirtyChestMenu menu = CargoUtils.getChestMenu(inputTarget); + // Try to add the item into another available slot then + ItemStack rest = inv.addItem(item).get(0); - if (menu != null) { - if (menu.getItemInSlot(previousSlot) == null) { - menu.replaceExistingItem(previousSlot, stack); - } else { - inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), stack); - } + if (rest != null && !manager.isItemDeletionEnabled()) { + // If the item still couldn't be inserted, simply drop it on the ground + inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), rest); + } + } + } else { + DirtyChestMenu menu = CargoUtils.getChestMenu(inputTarget); + + if (menu != null) { + if (menu.getItemInSlot(previousSlot) == null) { + menu.replaceExistingItem(previousSlot, item); + } else if (!manager.isItemDeletionEnabled()) { + inputTarget.getWorld().dropItem(inputTarget.getLocation().add(0, 1, 0), item); } } } @@ -149,7 +160,7 @@ class CargoNetworkTask implements Runnable { Optional target = network.getAttachedBlock(output); if (target.isPresent()) { - item = CargoUtils.insert(inventories, output.getBlock(), target.get(), item); + item = CargoUtils.insert(network, inventories, output.getBlock(), target.get(), item); if (item == null) { break; 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 b7e5fabab..12d1ced7f 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 @@ -1,7 +1,6 @@ package io.github.thebusybiscuit.slimefun4.core.networks.cargo; import java.util.Map; -import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -17,23 +16,21 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; -import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import io.papermc.lib.PaperLib; -import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.Slimefun.api.BlockStorage; -import me.mrCookieSlime.Slimefun.api.Slimefun; -import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu; import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; final class CargoUtils { - // Whitelist or blacklist slots - private static final int[] FILTER_SLOTS = { 19, 20, 21, 28, 29, 30, 37, 38, 39 }; + /** + * These are the slots where our filter items sit. + */ + static final int[] FILTER_SLOTS = { 19, 20, 21, 28, 29, 30, 37, 38, 39 }; private CargoUtils() {} @@ -117,7 +114,7 @@ final class CargoUtils { } } - static ItemStack withdraw(Map inventories, Block node, Block target, ItemStack template) { + static ItemStack withdraw(AbstractItemNetwork network, Map inventories, Block node, Block target, ItemStack template) { DirtyChestMenu menu = getChestMenu(target); if (menu == null) { @@ -125,7 +122,7 @@ final class CargoUtils { Inventory inventory = inventories.get(target.getLocation()); if (inventory != null) { - return withdrawFromVanillaInventory(node, template, inventory); + return withdrawFromVanillaInventory(network, node, template, inventory); } BlockState state = PaperLib.getBlockState(target, false).getState(); @@ -133,7 +130,7 @@ final class CargoUtils { if (state instanceof InventoryHolder) { inventory = ((InventoryHolder) state).getInventory(); inventories.put(target.getLocation(), inventory); - return withdrawFromVanillaInventory(node, template, inventory); + return withdrawFromVanillaInventory(network, node, template, inventory); } } @@ -145,7 +142,7 @@ final class CargoUtils { for (int slot : menu.getPreset().getSlotsAccessedByItemTransport(menu, ItemTransportFlow.WITHDRAW, null)) { ItemStack is = menu.getItemInSlot(slot); - if (SlimefunUtils.isItemSimilar(is, wrapper, true) && matchesFilter(node, is)) { + if (SlimefunUtils.isItemSimilar(is, wrapper, true) && matchesFilter(network, node, is)) { if (is.getAmount() > template.getAmount()) { is.setAmount(is.getAmount() - template.getAmount()); menu.replaceExistingItem(slot, is.clone()); @@ -160,7 +157,7 @@ final class CargoUtils { return null; } - static ItemStack withdrawFromVanillaInventory(Block node, ItemStack template, Inventory inv) { + static ItemStack withdrawFromVanillaInventory(AbstractItemNetwork network, Block node, ItemStack template, Inventory inv) { ItemStack[] contents = inv.getContents(); int[] range = getOutputSlotRange(inv); int minSlot = range[0]; @@ -172,7 +169,7 @@ final class CargoUtils { // Changes to these ItemStacks are synchronized with the Item in the Inventory ItemStack itemInSlot = contents[slot]; - if (SlimefunUtils.isItemSimilar(itemInSlot, wrapper, true, false) && matchesFilter(node, itemInSlot)) { + if (SlimefunUtils.isItemSimilar(itemInSlot, wrapper, true, false) && matchesFilter(network, node, itemInSlot)) { if (itemInSlot.getAmount() > template.getAmount()) { itemInSlot.setAmount(itemInSlot.getAmount() - template.getAmount()); return template; @@ -187,14 +184,14 @@ final class CargoUtils { return null; } - static ItemStackAndInteger withdraw(Map inventories, Block node, Block target) { + static ItemStackAndInteger withdraw(AbstractItemNetwork network, Map inventories, Block node, Block target) { DirtyChestMenu menu = getChestMenu(target); if (menu != null) { for (int slot : menu.getPreset().getSlotsAccessedByItemTransport(menu, ItemTransportFlow.WITHDRAW, null)) { ItemStack is = menu.getItemInSlot(slot); - if (matchesFilter(node, is)) { + if (matchesFilter(network, node, is)) { menu.replaceExistingItem(slot, null); return new ItemStackAndInteger(is, slot); } @@ -203,7 +200,7 @@ final class CargoUtils { Inventory inventory = inventories.get(target.getLocation()); if (inventory != null) { - return withdrawFromVanillaInventory(node, inventory); + return withdrawFromVanillaInventory(network, node, inventory); } BlockState state = PaperLib.getBlockState(target, false).getState(); @@ -211,14 +208,14 @@ final class CargoUtils { if (state instanceof InventoryHolder) { inventory = ((InventoryHolder) state).getInventory(); inventories.put(target.getLocation(), inventory); - return withdrawFromVanillaInventory(node, inventory); + return withdrawFromVanillaInventory(network, node, inventory); } } return null; } - private static ItemStackAndInteger withdrawFromVanillaInventory(Block node, Inventory inv) { + private static ItemStackAndInteger withdrawFromVanillaInventory(AbstractItemNetwork network, Block node, Inventory inv) { ItemStack[] contents = inv.getContents(); int[] range = getOutputSlotRange(inv); int minSlot = range[0]; @@ -227,7 +224,7 @@ final class CargoUtils { for (int slot = minSlot; slot < maxSlot; slot++) { ItemStack is = contents[slot]; - if (matchesFilter(node, is)) { + if (matchesFilter(network, node, is)) { inv.setItem(slot, null); return new ItemStackAndInteger(is, slot); } @@ -236,8 +233,8 @@ final class CargoUtils { return null; } - static ItemStack insert(Map inventories, Block node, Block target, ItemStack stack) { - if (!matchesFilter(node, stack)) { + static ItemStack insert(AbstractItemNetwork network, Map inventories, Block node, Block target, ItemStack stack) { + if (!matchesFilter(network, node, stack)) { return stack; } @@ -338,77 +335,12 @@ final class CargoUtils { return BlockStorage.getUniversalInventory(block); } - static boolean matchesFilter(@Nonnull Block block, @Nullable ItemStack item) { + static boolean matchesFilter(@Nonnull AbstractItemNetwork network, @Nonnull Block node, @Nullable ItemStack item) { if (item == null || item.getType() == Material.AIR) { return false; } - // Store the returned Config instance to avoid heavy calls - Config blockData = BlockStorage.getLocationInfo(block.getLocation()); - String id = blockData.getString("id"); - - if (id == null) { - // This should normally not happen but if it does... - // Don't accept any items. - return false; - } else if (id.equals("CARGO_NODE_OUTPUT")) { - // Cargo Output nodes have no filter actually - return true; - } - - try { - BlockMenu menu = BlockStorage.getInventory(block.getLocation()); - - if (menu == null) { - return false; - } - - boolean lore = "true".equals(blockData.getString("filter-lore")); - boolean allowByDefault = !"whitelist".equals(blockData.getString("filter-type")); - return matchesFilterList(item, menu, lore, allowByDefault); - } catch (Exception x) { - Slimefun.getLogger().log(Level.SEVERE, x, () -> "An Exception occurred while trying to filter items for a Cargo Node (" + id + ") at " + new BlockPosition(block)); - return false; - } - } - - private static boolean matchesFilterList(ItemStack item, BlockMenu menu, boolean respectLore, boolean defaultValue) { - // Little performance optimization: - // First check if there is more than one item to compare, if so - // then we know we should create an ItemStackWrapper, otherwise it would - // be of no benefit to us and just be redundant - int itemsToCompare = 0; - - for (int slot : FILTER_SLOTS) { - ItemStack stack = menu.getItemInSlot(slot); - - if (stack != null && stack.getType() != Material.AIR) { - itemsToCompare++; - - if (itemsToCompare > 1) { - break; - } - } - } - - // Check if there are event non-air items - if (itemsToCompare > 0) { - // Only create the Wrapper if its worth it - if (itemsToCompare > 1) { - // Create an itemStackWrapper to save performance - item = new ItemStackWrapper(item); - } - - for (int slot : FILTER_SLOTS) { - ItemStack stack = menu.getItemInSlot(slot); - - if (SlimefunUtils.isItemSimilar(stack, item, respectLore, false)) { - return !defaultValue; - } - } - } - - return defaultValue; + return network.getItemFilter(node).test(item); } /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemFilter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemFilter.java new file mode 100644 index 000000000..778e92edc --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/ItemFilter.java @@ -0,0 +1,191 @@ +package io.github.thebusybiscuit.slimefun4.core.networks.cargo; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; + +import javax.annotation.Nonnull; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; +import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; +import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; +import me.mrCookieSlime.Slimefun.api.BlockStorage; +import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; + +/** + * The {@link ItemFilter} is a performance-optimization for our {@link CargoNet}. + * It is a snapshot of a cargo node's configuration. + * + * @author TheBusyBiscuit + * + * @see CargoNet + * @see CargoNetworkTask + * + */ +class ItemFilter implements Predicate { + + /** + * Our {@link List} of items to check against, might be empty. + * This has a maximum capacity of 9. + */ + private final List items = new ArrayList<>(9); + + /** + * Our default value for this {@link ItemFilter}. + * A default value of {@literal true} will mean that it returns true if no + * match was found. It will deny any items that match. + * A default value of {@literal false} means that it will return false if no + * match was found. Only items that match will make it past this {@link ItemFilter}. + */ + private boolean rejectOnMatch; + + /** + * Whether we should also compare the lore. + */ + private boolean checkLore; + + /** + * If an {@link ItemFilter} is marked as dirty / outdated, then it will be updated + * on the next tick. + */ + private boolean dirty = false; + + /** + * This creates a new {@link ItemFilter} for the given {@link Block}. + * This will copy all settings from that {@link Block} to this filter. + * + * @param b + * The {@link Block} + */ + public ItemFilter(@Nonnull Block b) { + update(b); + } + + /** + * This updates or refreshes the {@link ItemFilter} to copy the settings + * from the given {@link Block}. It takes a new snapshot. + * + * @param b + * The {@link Block} + */ + public void update(@Nonnull Block b) { + // Store the returned Config instance to avoid heavy calls + Config blockData = BlockStorage.getLocationInfo(b.getLocation()); + String id = blockData.getString("id"); + SlimefunItem item = SlimefunItem.getByID(id); + BlockMenu menu = BlockStorage.getInventory(b.getLocation()); + + if (item == null || menu == null) { + // Don't filter for a non-existing item (safety check) + clear(false); + } else if (id.equals("CARGO_NODE_OUTPUT")) { + // Output Nodes have no filter, allow everything + clear(true); + } else { + this.items.clear(); + this.checkLore = Objects.equals(blockData.getString("filter-lore"), "true"); + this.rejectOnMatch = !Objects.equals(blockData.getString("filter-type"), "whitelist"); + + for (int slot : CargoUtils.FILTER_SLOTS) { + ItemStack stack = menu.getItemInSlot(slot); + + if (stack != null && stack.getType() != Material.AIR) { + this.items.add(new ItemStackWrapper(stack)); + } + } + } + + this.dirty = false; + } + + /** + * This will clear the {@link ItemFilter} and reject any + * {@link ItemStack}. + * + * @param rejectOnMatch + * Whether the item should be rejected on matches + */ + private void clear(boolean rejectOnMatch) { + this.items.clear(); + this.checkLore = false; + this.rejectOnMatch = rejectOnMatch; + } + + /** + * Whether this {@link ItemFilter} is outdated and needs to be refreshed. + * + * @return Whether the filter is outdated. + */ + public boolean isDirty() { + return this.dirty; + } + + /** + * This marks this {@link ItemFilter} as dirty / outdated. + */ + public void markDirty() { + this.dirty = true; + } + + @Override + public boolean test(@Nonnull ItemStack item) { + /** + * An empty Filter does not need to be iterated over. + * We can just return our default value in this scenario. + */ + if (items.isEmpty()) { + return rejectOnMatch; + } + + // The amount of potential matches with that item. + int potentialMatches = 0; + + /* + * This is a first check for materials to see if we might even have any match. + * If there is no potential match then we won't need to perform the quite + * intense operation .getItemMeta() + */ + for (ItemStackWrapper stack : items) { + if (stack.getType() == item.getType()) { + // We found a potential match based on the Material + potentialMatches++; + } + } + + if (potentialMatches == 0) { + // If there is no match, we can safely assume the default value + return rejectOnMatch; + } else { + /* + * If there is more than one potential match, create a wrapper to save + * performance on the ItemMeta otherwise just use the item directly. + */ + ItemStack subject = potentialMatches == 1 ? item : new ItemStackWrapper(item); + + /* + * If there is only one match, we won't need to create a Wrapper + * and thus only perform .getItemMeta() once + */ + for (ItemStackWrapper stack : items) { + if (SlimefunUtils.isItemSimilar(subject, stack, checkLore, false)) { + /* + * The filter has found a match, we can return the opposite + * of our default value. If we exclude items, this is where we + * would return false. Otherwise we return true. + */ + return !rejectOnMatch; + } + } + + // If no particular item was matched, we fallback to our default value. + return rejectOnMatch; + } + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java index 02eea2d4b..0e659e474 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java @@ -15,7 +15,6 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; -import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler; import io.github.thebusybiscuit.slimefun4.api.ErrorReport; import io.github.thebusybiscuit.slimefun4.api.network.Network; import io.github.thebusybiscuit.slimefun4.api.network.NetworkComponent; @@ -23,6 +22,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetProvider; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import io.github.thebusybiscuit.slimefun4.utils.holograms.SimpleHologram; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; @@ -210,14 +210,14 @@ public class EnergyNet extends Network { SlimefunItem item = (SlimefunItem) provider; try { - Config config = BlockStorage.getLocationInfo(loc); - int energy = provider.getGeneratedOutput(loc, config); + Config data = BlockStorage.getLocationInfo(loc); + int energy = provider.getGeneratedOutput(loc, data); if (provider.isChargeable()) { - energy += provider.getCharge(loc); + energy += provider.getCharge(loc, data); } - if (provider.willExplode(loc, config)) { + if (provider.willExplode(loc, data)) { explodedBlocks.add(loc); BlockStorage.clearBlockInfo(loc); @@ -228,9 +228,9 @@ public class EnergyNet extends Network { } else { supply += energy; } - } catch (Exception | LinkageError t) { + } catch (Exception | LinkageError throwable) { explodedBlocks.add(loc); - new ErrorReport<>(t, loc, item); + new ErrorReport<>(throwable, loc, item); } long time = SlimefunPlugin.getProfiler().closeEntry(loc, item, timestamp); @@ -238,7 +238,10 @@ public class EnergyNet extends Network { } // Remove all generators which have exploded - generators.keySet().removeAll(explodedBlocks); + if (!explodedBlocks.isEmpty()) { + generators.keySet().removeAll(explodedBlocks); + } + return supply; } @@ -254,10 +257,10 @@ public class EnergyNet extends Network { private void updateHologram(@Nonnull Block b, double supply, double demand) { if (demand > supply) { - String netLoss = DoubleHandler.getFancyDouble(Math.abs(supply - demand)); + String netLoss = NumberUtils.getCompactDouble(demand - supply); SimpleHologram.update(b, "&4&l- &c" + netLoss + " &7J &e\u26A1"); } else { - String netGain = DoubleHandler.getFancyDouble(supply - demand); + String netGain = NumberUtils.getCompactDouble(supply - demand); SimpleHologram.update(b, "&2&l+ &a" + netGain + " &7J &e\u26A1"); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/researching/Research.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/researching/Research.java index 569845ac4..191d1c69f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/researching/Research.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/researching/Research.java @@ -117,6 +117,7 @@ public class Research implements Keyed { * * @param p * The {@link Player} to translate this name for. + * * @return The localized Name of this {@link Research}. */ @Nonnull @@ -197,12 +198,18 @@ public class Research implements Keyed { * Handle what to do when a {@link Player} clicks on an un-researched item in * a {@link SlimefunGuideImplementation}. * - * @param guide The {@link SlimefunGuideImplementation} used. - * @param player The {@link Player} who clicked on the item. - * @param profile The {@link PlayerProfile} of that {@link Player}. - * @param sfItem The {@link SlimefunItem} on which the {@link Player} clicked. - * @param category The {@link Category} where the {@link Player} was. - * @param page The page number of where the {@link Player} was in the {@link Category}; + * @param guide + * The {@link SlimefunGuideImplementation} used. + * @param player + * The {@link Player} who clicked on the item. + * @param profile + * The {@link PlayerProfile} of that {@link Player}. + * @param sfItem + * The {@link SlimefunItem} on which the {@link Player} clicked. + * @param category + * The {@link Category} where the {@link Player} was. + * @param page + * The page number of where the {@link Player} was in the {@link Category}; * */ @ParametersAreNonnullByDefault @@ -230,6 +237,7 @@ public class Research implements Keyed { * * @param p * The {@link Player} to check + * * @return Whether that {@link Player} can unlock this {@link Research} */ public boolean canUnlock(@Nonnull Player p) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java index d965cefde..b28c6d086 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java @@ -77,8 +77,9 @@ public class AutoSavingService { Set worlds = new HashSet<>(); for (World world : Bukkit.getWorlds()) { - if (BlockStorage.isWorldRegistered(world.getName())) { - BlockStorage storage = BlockStorage.getStorage(world); + BlockStorage storage = BlockStorage.getStorage(world); + + if (storage != null) { storage.computeChanges(); if (storage.getChanges() > 0) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java index 98a8b2853..feea594a0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/BlockDataService.java @@ -7,7 +7,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang.Validate; - import org.bukkit.Bukkit; import org.bukkit.Keyed; import org.bukkit.Material; @@ -20,10 +19,7 @@ import org.bukkit.persistence.PersistentDataHolder; import org.bukkit.persistence.PersistentDataType; import org.bukkit.plugin.Plugin; -import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; -import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.papermc.lib.PaperLib; -import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult; import me.mrCookieSlime.Slimefun.api.Slimefun; /** @@ -70,20 +66,17 @@ public class BlockDataService implements Keyed { Validate.notNull(b, "The block cannot be null!"); Validate.notNull(value, "The value cannot be null!"); - // Due to a bug on older versions, Persistent Data is nullable for non-snapshots - boolean useSnapshot = SlimefunPlugin.getMinecraftVersion().isBefore(MinecraftVersion.MINECRAFT_1_16); - - BlockStateSnapshotResult result = PaperLib.getBlockState(b, useSnapshot); - BlockState state = result.getState(); + /** + * Don't use PaperLib here, it seems to be quite buggy in block-placing scenarios + * and it would be too tedious to check for individual build versions to circumvent this. + */ + BlockState state = b.getState(); if (state instanceof TileState) { try { PersistentDataContainer container = ((TileState) state).getPersistentDataContainer(); container.set(namespacedKey, PersistentDataType.STRING, value); - - if (result.isSnapshot()) { - state.update(); - } + state.update(); } catch (Exception x) { Slimefun.getLogger().log(Level.SEVERE, "Please check if your Server Software is up to date!"); @@ -100,21 +93,31 @@ public class BlockDataService implements Keyed { * * @param b * The {@link Block} to retrieve data from + * * @return The stored value */ public Optional getBlockData(@Nonnull Block b) { Validate.notNull(b, "The block cannot be null!"); BlockState state = PaperLib.getBlockState(b, false).getState(); + PersistentDataContainer container = getPersistentDataContainer(state); - if (state instanceof TileState) { - PersistentDataContainer container = ((TileState) state).getPersistentDataContainer(); + if (container != null) { return Optional.ofNullable(container.get(namespacedKey, PersistentDataType.STRING)); } else { return Optional.empty(); } } + @Nullable + private PersistentDataContainer getPersistentDataContainer(@Nonnull BlockState state) { + if (state instanceof TileState) { + return ((TileState) state).getPersistentDataContainer(); + } else { + return null; + } + } + /** * This method checks whether the given {@link Material} is a Tile Entity. * This is used to determine whether the {@link Block} produced by this {@link Material} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomTextureService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomTextureService.java index 3caea1cbb..854cc86ab 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomTextureService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomTextureService.java @@ -23,9 +23,22 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; */ public class CustomTextureService { + /** + * The {@link Config} object in which the Server Owner can configure the item models. + */ private final Config config; + /** + * This nullable {@link StringBuffer} represents the "version" of the used item-models file. + * This version is served with our resource pack. + */ private String version = null; + + /** + * This boolean represents whether the file was modified anyway. + * This is equivalent to at least one value being set to a number which + * is not zero! + */ private boolean modified = false; /** @@ -110,6 +123,7 @@ public class CustomTextureService { */ public int getModelData(@Nonnull String id) { Validate.notNull(id, "Cannot get the ModelData for 'null'"); + return config.getInt(id); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java index 74ddcd2e1..ad4bee64f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java @@ -140,7 +140,7 @@ public class MetricsService { } /** - * This will close the child classloader and mark all the resources held under this no longer + * This will close the child {@link ClassLoader} and mark all the resources held under this no longer * in use, they will be cleaned up the next GC run. */ public void cleanUp() { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java index 46c409fa5..b323d3f2f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java @@ -24,8 +24,20 @@ import io.github.thebusybiscuit.slimefun4.utils.PatternUtils; */ public class UpdaterService { + /** + * Our {@link SlimefunPlugin} instance. + */ private final SlimefunPlugin plugin; + + /** + * Our {@link Updater} implementation. + */ private final Updater updater; + + /** + * The {@link SlimefunBranch} we are currently on. + * If this is an official {@link SlimefunBranch}, auto updates will be enabled. + */ private final SlimefunBranch branch; /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/Contributor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/Contributor.java index d558502ad..0bc34a0fa 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/Contributor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/Contributor.java @@ -38,7 +38,7 @@ public class Contributor { private final ComputedOptional headTexture = ComputedOptional.createNew(); private Optional uuid = Optional.empty(); - private boolean locked = false; + private boolean immutable = false; /** * This creates a new {@link Contributor} with the given ingame name and GitHub profile. @@ -76,7 +76,7 @@ public class Contributor { * specified role. * * @param role - * The role + * The role of this {@link Contributor} * @param commits * The amount of contributions made as that role */ @@ -84,7 +84,7 @@ public class Contributor { Validate.notNull(role, "The role cannot be null!"); Validate.isTrue(commits >= 0, "Contributions cannot be negative"); - if (!locked || role.startsWith("translator,")) { + if (!immutable || role.startsWith("translator,")) { contributions.put(role, commits); } } @@ -120,6 +120,13 @@ public class Contributor { return profileLink; } + /** + * This returns a {@link List} of contributions for this {@link Contributor}. + * Each entry consists of a {@link String} (for the role) and an {@link Integer} + * (for the amount of commits). + * + * @return A {@link List} of contributions for this {@link Contributor} + */ @Nonnull public List> getContributions() { List> list = new ArrayList<>(contributions.entrySet()); @@ -137,6 +144,8 @@ public class Contributor { * @return The amount of contributions this {@link Contributor} submitted as the given role */ public int getContributions(@Nonnull String role) { + Validate.notNull(role, "The role cannot be null!"); + return contributions.getOrDefault(role, 0); } @@ -214,16 +223,35 @@ public class Contributor { return contributions.values().stream().mapToInt(Integer::intValue).sum(); } - public int index() { - return -getTotalContributions(); - } - + /** + * This returns the final display name for this {@link Contributor}. + * The display name is basically the GitHub username but if the Minecraft username differs, + * it will be appended in brackets behind the GitHub username. + * + * @return The final display name of this {@link Contributor}. + */ @Nonnull public String getDisplayName() { return ChatColor.GRAY + githubUsername + (!githubUsername.equals(minecraftUsername) ? ChatColor.DARK_GRAY + " (MC: " + minecraftUsername + ")" : ""); } - public void lock() { - locked = true; + /** + * This returns the position on where to order this {@link Contributor}. + * This is just a convenience method for a {@link Comparator}, it is equivalent to + * {@link #getTotalContributions()} multiplied by minus one. + * + * @return The position of this {@link Contributor} in terms for positioning and ordering. + */ + public int getPosition() { + return -getTotalContributions(); + } + + /** + * This marks this {@link Contributor} as immutable. + * Immutable {@link Contributor Contributors} will no longer be assigned any contributions. + * This is useful when you want to prevent some commits from counting twice. + */ + public void setImmutable() { + immutable = true; } } 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 f7d103f24..29b66a083 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 @@ -8,6 +8,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -37,6 +38,7 @@ public class GitHubService { private boolean logging = false; private LocalDateTime lastUpdate = LocalDateTime.now(); + private int openIssues = 0; private int pendingPullRequests = 0; private int publicForks = 0; @@ -56,9 +58,18 @@ public class GitHubService { loadConnectors(false); } + /** + * This will start the {@link GitHubService} and run the asynchronous {@link GitHubTask} + * every so often to update its data. + * + * @param plugin + * Our instance of {@link SlimefunPlugin} + */ public void start(@Nonnull SlimefunPlugin plugin) { + long period = TimeUnit.HOURS.toMillis(1); GitHubTask task = new GitHubTask(this); - plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, task, 80L, 60 * 60 * 20L); + + plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, task, 80L, period); } /** @@ -215,8 +226,16 @@ public class GitHubService { texturesCache.save(); } + /** + * This returns the cached skin texture for a given username. + * + * @param username + * The minecraft username + * + * @return The cached skin texture for that user (or null) + */ @Nullable - protected String getCachedTexture(@Nonnull String name) { - return texturesCache.getString(name); + protected String getCachedTexture(@Nonnull String username) { + return texturesCache.getString(username); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java index 151afa635..26e1f2c63 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java @@ -39,17 +39,23 @@ class GitHubTask implements Runnable { @Override public void run() { - gitHubService.getConnectors().forEach(GitHubConnector::download); + connectAndCache(); grabTextures(); } + private void connectAndCache() { + gitHubService.getConnectors().forEach(GitHubConnector::download); + } + /** * This method will pull the skin textures for every {@link Contributor} and store * the {@link UUID} and received skin inside a local cache {@link File}. */ private void grabTextures() { - // Store all queried usernames to prevent 429 responses for pinging the - // same URL twice in one run. + /** + * Store all queried usernames to prevent 429 responses for pinging + * the same URL twice in one run. + */ Map skins = new HashMap<>(); int requests = 0; @@ -73,8 +79,11 @@ class GitHubTask implements Runnable { } } - // We only wanna save this if all Connectors finished already - // This will run multiple times but thats okay, this way we get as much data as possible stored + /** + * We only wanna save this if all Connectors finished already. + * This will run multiple times but thats okay, this way we get as much + * data as possible stored. + */ gitHubService.saveCache(); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Translators.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Translators.java index c82fea35f..3b4bfced1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Translators.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/Translators.java @@ -165,12 +165,17 @@ public class Translators { // Translators - Tagalog addTranslator("sccooottttie", SupportedLanguage.TAGALOG, true); + // Translators - Portuguese + addTranslator("Gusstavo", SupportedLanguage.PORTUGUESE_PORTUGAL, true); + // Translators - Portuguese (Brazil) addTranslator("G4stavoM1ster", SupportedLanguage.PORTUGUESE_BRAZIL, true); addTranslator("yurinogueira", SupportedLanguage.PORTUGUESE_BRAZIL, true); addTranslator("Sakanas", SupportedLanguage.PORTUGUESE_BRAZIL, true); addTranslator("krazybeat", SupportedLanguage.PORTUGUESE_BRAZIL, true); addTranslator("FaolanMalcadh", SupportedLanguage.PORTUGUESE_BRAZIL, true); + addTranslator("G4stavoM1ster", SupportedLanguage.PORTUGUESE_BRAZIL, true); + addTranslator("Gusstavo", SupportedLanguage.PORTUGUESE_BRAZIL, true); } @ParametersAreNonnullByDefault @@ -183,7 +188,7 @@ public class Translators { Contributor contributor = github.addContributor(minecraftName, "https://github.com/" + username, "translator," + lang.getLanguageId(), 0); if (lock) { - contributor.lock(); + contributor.setImmutable(); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/EmeraldEnchantsCategory.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/EmeraldEnchantsCategory.java deleted file mode 100644 index 486c59af8..000000000 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/EmeraldEnchantsCategory.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.thebusybiscuit.slimefun4.core.services.plugins; - -import javax.annotation.Nonnull; - -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.entity.Player; - -import io.github.thebusybiscuit.cscorelib2.item.CustomItem; -import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; -import io.github.thebusybiscuit.slimefun4.core.categories.FlexCategory; -import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideLayout; -import me.mrCookieSlime.EmeraldEnchants.EnchantmentGuide; - -@Deprecated -class EmeraldEnchantsCategory extends FlexCategory { - - public EmeraldEnchantsCategory(@Nonnull NamespacedKey key) { - super(key, new CustomItem(Material.ENCHANTED_BOOK, "&2EmeraldEnchants &a(Enchantment Guide)"), 2); - } - - @Override - public void open(Player p, PlayerProfile profile, SlimefunGuideLayout layout) { - EnchantmentGuide.open(p); - } - - @Override - public boolean isVisible(Player p, PlayerProfile profile, SlimefunGuideLayout layout) { - return true; - } - -} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/ThirdPartyPluginService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/ThirdPartyPluginService.java index 1b4e5e1a4..103b1cf75 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/ThirdPartyPluginService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/ThirdPartyPluginService.java @@ -7,7 +7,6 @@ import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; -import org.bukkit.NamespacedKey; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.inventory.ItemStack; @@ -16,7 +15,6 @@ import org.bukkit.plugin.Plugin; import com.gmail.nossr50.events.fake.FakeBlockBreakEvent; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; -import io.github.thebusybiscuit.slimefun4.core.categories.FlexCategory; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import me.mrCookieSlime.Slimefun.api.Slimefun; @@ -38,7 +36,6 @@ public class ThirdPartyPluginService { private boolean initialized = false; private boolean isExoticGardenInstalled = false; private boolean isChestTerminalInstalled = false; - private boolean isEmeraldEnchantsInstalled = false; private boolean isMcMMOInstalled = false; /** @@ -78,13 +75,6 @@ public class ThirdPartyPluginService { } } - if (isPluginInstalled("EmeraldEnchants")) { - isEmeraldEnchantsInstalled = true; - Plugin emeraldEnchants = plugin.getServer().getPluginManager().getPlugin("EmeraldEnchants"); - FlexCategory category = new EmeraldEnchantsCategory(new NamespacedKey(emeraldEnchants, "enchantment_guide")); - category.register(); - } - // WorldEdit Hook to clear Slimefun Data upon //set 0 //cut or any other equivalent if (isPluginInstalled("WorldEdit")) { try { @@ -149,10 +139,6 @@ public class ThirdPartyPluginService { return isChestTerminalInstalled; } - public boolean isEmeraldEnchantsInstalled() { - return isEmeraldEnchantsInstalled; - } - public Optional harvestExoticGardenPlant(Block block) { return exoticGardenIntegration.apply(block); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceRating.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceRating.java index b1417b523..130898eab 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceRating.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/PerformanceRating.java @@ -45,7 +45,7 @@ public enum PerformanceRating implements Predicate { @Override public boolean test(@Nullable Float value) { if (value == null) { - // null will only test true for UNKNOWN + // This way null will only test true for UNKNOWN return threshold < 0; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/ProfiledBlock.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/ProfiledBlock.java index 8a4e68d0d..6348d0d07 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/ProfiledBlock.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/ProfiledBlock.java @@ -1,8 +1,11 @@ package io.github.thebusybiscuit.slimefun4.core.services.profiler; +import java.util.Objects; + import javax.annotation.Nonnull; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.block.Block; import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition; @@ -11,44 +14,129 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; /** * This represents an entry in our {@link SlimefunProfiler}. + * It is a modification of {@link BlockPosition} to be as memory-efficient as possible. * * @author TheBusyBiscuit * */ -class ProfiledBlock { +final class ProfiledBlock { - private final BlockPosition position; + /** + * The {@link World} this {@link Block} is in. + * It is fine to keep an actual reference here since this is a throwaway object anyway. + */ + private final World world; + + /** + * A {@link Long} representation of our {@link Location} (x, y, z). + */ + private final long position; + + /** + * The {@link SlimefunItem} whihc is located at this {@link Location}. + */ private final SlimefunItem item; + /** + * This creates a new {@link ProfiledBlock} for the given {@link Location} and + * the {@link SlimefunItem} found at this {@link Location}. + * + * @param l + * The {@link Location} + * @param item + * The {@link SlimefunItem} found at that {@link Location} + */ ProfiledBlock(@Nonnull Location l, @Nonnull SlimefunItem item) { - this.position = new BlockPosition(l); - this.item = item; - } - - ProfiledBlock(@Nonnull BlockPosition position, @Nonnull SlimefunItem item) { - this.position = position; + this.world = l.getWorld(); + this.position = getLocationAsLong((int) l.getX(), (int) l.getY(), (int) l.getZ()); this.item = item; } /** * This is just a dummy constructor. + * Please only use this for comparisons or lookups. * * @param b * A {@link Block} */ ProfiledBlock(@Nonnull Block b) { - this.position = new BlockPosition(b); + this.world = b.getWorld(); + this.position = getLocationAsLong(b.getX(), b.getY(), b.getZ()); this.item = null; } - public BlockPosition getPosition() { - return position; + /** + * This compresses our {@link Location} into a long for more efficient memory usage + * + * @param x + * The x value + * @param y + * The y value + * @param z + * The z value + * + * @return A {@link Long} representation of this {@link Location} + */ + private static long getLocationAsLong(int x, int y, int z) { + return ((long) (x & 0x3FFFFFF) << 38) | ((long) (z & 0x3FFFFFF) << 12) | (long) (y & 0xFFF); } + @Nonnull + public World getWorld() { + return world; + } + + /** + * Gets the x for this block. + * + * @return This blocks x coordinate. + */ + public int getX() { + return (int) (this.position >> 38); + } + + /** + * Gets the y for this block. + * + * @return This blocks y coordinate. + */ + public int getY() { + return (int) (this.position & 0xFFF); + } + + /** + * Gets the z for this block. + * + * @return This blocks z coordinate. + */ + public int getZ() { + return (int) (this.position << 26 >> 38); + } + + /** + * Gets the chunks x coordinate for this block. + * + * @return The blocks chunks x coordinate. + */ + public int getChunkX() { + return this.getX() >> 4; + } + + /** + * Gets the chunks z coordinate for this block. + * + * @return The blocks chunks z coordinate. + */ + public int getChunkZ() { + return this.getZ() >> 4; + } + + @Nonnull public String getId() { return item.getId(); } + @Nonnull public SlimefunAddon getAddon() { return item.getAddon(); } @@ -56,7 +144,8 @@ class ProfiledBlock { @Override public boolean equals(Object obj) { if (obj instanceof ProfiledBlock) { - return position.equals(((ProfiledBlock) obj).position); + ProfiledBlock block = (ProfiledBlock) obj; + return position == block.position && Objects.equals(world, block.world); } return false; @@ -64,7 +153,8 @@ class ProfiledBlock { @Override public int hashCode() { - return position.hashCode(); + long hilo = world.getUID().getMostSignificantBits() ^ world.getUID().getLeastSignificantBits(); + return (int) (position ^ (position >> 32) ^ hilo ^ (hilo >> 32)); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunProfiler.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunProfiler.java index 5a0d92d9a..514b4d32e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunProfiler.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunProfiler.java @@ -20,6 +20,7 @@ import org.bukkit.Location; import org.bukkit.Server; import org.bukkit.block.Block; import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitScheduler; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; @@ -42,12 +43,27 @@ import me.mrCookieSlime.Slimefun.api.Slimefun; */ public class SlimefunProfiler { - // A minecraft server tick is 50ms and Slimefun ticks are stretched across - // two ticks (sync and async blocks), so we use 100ms as a reference here + /** + * A minecraft server tick is 50ms and Slimefun ticks are stretched + * across two ticks (sync and async blocks), so we use 100ms as a reference here + */ private static final int MAX_TICK_DURATION = 100; - private final ExecutorService executor = Executors.newFixedThreadPool(5); - private final AtomicBoolean running = new AtomicBoolean(false); + /** + * Our internal instance of {@link SlimefunThreadFactory}, it provides the naming + * convention for our {@link Thread} pool and also the count of this pool. + */ + private final SlimefunThreadFactory threadFactory = new SlimefunThreadFactory(2); + + /** + * This is our {@link Thread} pool to evaluate timings data. + * We cannot use the {@link BukkitScheduler} here because we need to evaluate + * this data in split seconds. + * So we cannot simply wait until the next server tick for this. + */ + private final ExecutorService executor = Executors.newFixedThreadPool(threadFactory.getThreadCount(), threadFactory); + + private final AtomicBoolean isProfiling = new AtomicBoolean(false); private final AtomicInteger queued = new AtomicInteger(0); private long totalElapsedTime; @@ -55,11 +71,20 @@ public class SlimefunProfiler { private final Map timings = new ConcurrentHashMap<>(); private final Queue requests = new ConcurrentLinkedQueue<>(); + /** + * This method terminates the {@link SlimefunProfiler}. + * We need to call this method when the {@link Server} shuts down to prevent any + * of our {@link Thread Threads} from being kept alive. + */ + public void kill() { + executor.shutdown(); + } + /** * This method starts the profiling, data from previous runs will be cleared. */ public void start() { - running.set(true); + isProfiling.set(true); queued.set(0); timings.clear(); } @@ -70,7 +95,7 @@ public class SlimefunProfiler { * @return A timestamp, best fed back into {@link #closeEntry(Location, SlimefunItem, long)} */ public long newEntry() { - if (!running.get()) { + if (!isProfiling.get()) { return 0; } @@ -89,7 +114,7 @@ public class SlimefunProfiler { * The amount of entries that should be scheduled. Can be negative */ public void scheduleEntries(int amount) { - if (running.get()) { + if (isProfiling.get()) { queued.getAndAdd(amount); } } @@ -132,15 +157,13 @@ public class SlimefunProfiler { * This stops the profiling. */ public void stop() { - running.set(false); + isProfiling.set(false); if (SlimefunPlugin.instance() == null || !SlimefunPlugin.instance().isEnabled()) { // Slimefun has been disabled return; } - // Since we got more than one Thread in our pool, - // blocking this one is (hopefully) completely fine executor.execute(this::finishReport); } @@ -149,8 +172,12 @@ public class SlimefunProfiler { int iterations = 4000; // Wait for all timing results to come in - while (!running.get() && queued.get() > 0) { + while (!isProfiling.get() && queued.get() > 0) { try { + /** + * Since we got more than one Thread in our pool, + * blocking this one is (hopefully) completely fine + */ Thread.sleep(1); iterations--; @@ -162,6 +189,7 @@ public class SlimefunProfiler { iterator.next().sendMessage("Your timings report has timed out, we were still waiting for " + queued.get() + " samples to be collected :/"); iterator.remove(); } + return; } } catch (InterruptedException e) { @@ -170,7 +198,7 @@ public class SlimefunProfiler { } } - if (running.get() && queued.get() > 0) { + if (isProfiling.get() && queued.get() > 0) { // Looks like the next profiling has already started, abort! return; } @@ -228,9 +256,10 @@ public class SlimefunProfiler { Map map = new HashMap<>(); for (Map.Entry entry : timings.entrySet()) { - String world = entry.getKey().getPosition().getWorld().getName(); - int x = entry.getKey().getPosition().getChunkX(); - int z = entry.getKey().getPosition().getChunkZ(); + ProfiledBlock block = entry.getKey(); + String world = block.getWorld().getName(); + int x = block.getChunkX(); + int z = block.getChunkZ(); map.merge(world + " (" + x + ',' + z + ')', entry.getValue(), Long::sum); } @@ -243,9 +272,9 @@ public class SlimefunProfiler { int blocks = 0; for (ProfiledBlock block : timings.keySet()) { - String world = block.getPosition().getWorld().getName(); - int x = block.getPosition().getChunkX(); - int z = block.getPosition().getChunkZ(); + String world = block.getWorld().getName(); + int x = block.getChunkX(); + int z = block.getChunkZ(); if (chunk.equals(world + " (" + x + ',' + z + ')')) { blocks++; @@ -284,6 +313,7 @@ public class SlimefunProfiler { protected float getPercentageOfTick() { float millis = totalElapsedTime / 1000000.0F; float fraction = (millis * 100.0F) / MAX_TICK_DURATION; + return Math.round((fraction * 100.0F) / 100.0F); } @@ -305,6 +335,7 @@ public class SlimefunProfiler { return PerformanceRating.UNKNOWN; } + @Nonnull public String getTime() { return NumberUtils.getAsMillis(totalElapsedTime); } @@ -324,6 +355,7 @@ public class SlimefunProfiler { */ public boolean hasTimings(@Nonnull Block b) { Validate.notNull("Cannot get timings for a null Block"); + return timings.containsKey(new ProfiledBlock(b)); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunThreadFactory.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunThreadFactory.java new file mode 100644 index 000000000..75fad3ca6 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/profiler/SlimefunThreadFactory.java @@ -0,0 +1,49 @@ +package io.github.thebusybiscuit.slimefun4.core.services.profiler; + +import java.util.concurrent.ThreadFactory; + +import javax.annotation.Nonnull; + +/** + * This is our {@link ThreadFactory} for the {@link SlimefunProfiler}. + * It holds the amount of {@link Thread Threads} we dedicate towards our {@link SlimefunProfiler} + * and provides a naming convention for our {@link Thread Threads}. + * + * @author TheBusyBiscuit + * + * @see SlimefunProfiler + * + */ +final class SlimefunThreadFactory implements ThreadFactory { + + private final int threadCount; + + /** + * This constructs a new {@link SlimefunThreadFactory} with the given {@link Thread} count. + * + * @param threadCount + * The amount of {@link Thread Threads} to provide to the {@link SlimefunProfiler} + */ + SlimefunThreadFactory(int threadCount) { + this.threadCount = threadCount; + } + + /** + * This returns the amount of {@link Thread Threads} we dedicate towards + * the {@link SlimefunProfiler}. + * + * @return The {@link Thread} count + */ + int getThreadCount() { + return threadCount; + } + + /** + * This creates a new {@link Thread} for the {@link SlimefunProfiler}. + */ + @Override + public Thread newThread(@Nonnull Runnable runnable) { + return new Thread(runnable, "Slimefun Profiler"); + } + +} 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 a4224da9e..fab582ca1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunItems.java @@ -18,7 +18,7 @@ import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.core.attributes.MachineTier; import io.github.thebusybiscuit.slimefun4.core.attributes.MachineType; import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactivity; -import io.github.thebusybiscuit.slimefun4.implementation.items.magical.StormStaff; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.StormStaff; import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; import io.github.thebusybiscuit.slimefun4.utils.HeadTexture; import io.github.thebusybiscuit.slimefun4.utils.LoreBuilder; @@ -417,7 +417,7 @@ public final class SlimefunItems { 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 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.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 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"); public static final SlimefunItemStack WITHER_PROOF_OBSIDIAN = new SlimefunItemStack("WITHER_PROOF_OBSIDIAN", Material.OBSIDIAN, "&5Wither-Proof Obsidian", "", "&fWithstands Explosions", "&fWithstands Wither Bosses"); public static final SlimefunItemStack WITHER_PROOF_GLASS = new SlimefunItemStack("WITHER_PROOF_GLASS", Material.PURPLE_STAINED_GLASS, "&5Wither-Proof Glass", "", "&fWithstands Explosions", "&fWithstands Wither Bosses"); 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 6c473781a..c43c5209e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunPlugin.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/SlimefunPlugin.java @@ -12,12 +12,14 @@ import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.command.Command; import org.bukkit.entity.Player; +import org.bukkit.event.Listener; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.java.JavaPlugin; @@ -64,12 +66,13 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.BeeListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.BeeWingsListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockPhysicsListener; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.ButcherAndroidListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.CargoNodeListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.CoolerListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.DeathpointListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.DebugFishListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.DispenserListener; -import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraCrashListener; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraImpactListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.EnhancedFurnaceListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.EntityInteractionListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.ExplosionsListener; @@ -77,9 +80,11 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.FireworksList import io.github.thebusybiscuit.slimefun4.implementation.listeners.GadgetsListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.GrapplingHookListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.IronGolemListener; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemDropListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.ItemPickupListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.MobDropListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.MultiBlockListener; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.PiglinListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.PlayerProfileListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SeismicAxeListener; @@ -87,7 +92,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBoots import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunBowListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunGuideListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemConsumeListener; -import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemListener; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.SlimefunItemInteractListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SoulboundListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.TalismanListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.VampireBladeListener; @@ -109,7 +114,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.tasks.SlimefunStartupTa import io.github.thebusybiscuit.slimefun4.implementation.tasks.TickerTask; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import io.papermc.lib.PaperLib; -import me.mrCookieSlime.CSCoreLibPlugin.CSCoreLib; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AGenerator; import me.mrCookieSlime.Slimefun.api.BlockStorage; @@ -118,7 +122,6 @@ import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu; /** * This is the main class of Slimefun. * This is where all the magic starts, take a look around. - * Feel like home. * * @author TheBusyBiscuit */ @@ -163,15 +166,36 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { private final BackpackListener backpackListener = new BackpackListener(); private final SlimefunBowListener bowListener = new SlimefunBowListener(); + /** + * Our default constructor for {@link SlimefunPlugin}. + */ public SlimefunPlugin() { super(); } + /** + * This constructor is invoked in Unit Test environments only. + * + * @param loader + * Our {@link JavaPluginLoader} + * @param description + * A {@link PluginDescriptionFile} + * @param dataFolder + * The data folder + * @param file + * A {@link File} for this {@link Plugin} + */ + @ParametersAreNonnullByDefault public SlimefunPlugin(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) { super(loader, description, dataFolder, file); + + // This is only invoked during a Unit Test minecraftVersion = MinecraftVersion.UNIT_TEST; } + /** + * This is called when the {@link Plugin} has been loaded and enabled on a {@link Server}. + */ @Override public void onEnable() { instance = this; @@ -220,7 +244,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { networkSize = 1; } - networkManager = new NetworkManager(networkSize, config.getBoolean("networks.enable-visualizer")); + networkManager = new NetworkManager(networkSize, config.getBoolean("networks.enable-visualizer"), config.getBoolean("networks.delete-excess-items")); // Setting up bStats new Thread(metricsService::start, "Slimefun Metrics").start(); @@ -304,12 +328,87 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { } } + /** + * This is our start method for a Unit Test environment. + */ private void onUnitTestStart() { local = new LocalizationService(this, "", null); gpsNetwork = new GPSNetwork(); - networkManager = new NetworkManager(200, true); + networkManager = new NetworkManager(200); command.register(); registry.load(config); + loadTags(); + } + + /** + * This method gets called when the {@link Plugin} gets disabled. + * Most often it is called when the {@link Server} is shutting down or reloading. + */ + @Override + public void onDisable() { + // Slimefun never loaded successfully, so we don't even bother doing stuff here + if (instance() == null || minecraftVersion == MinecraftVersion.UNIT_TEST) { + return; + } + + // Cancel all tasks from this plugin immediately + Bukkit.getScheduler().cancelTasks(this); + + // Finishes all started movements/removals of block data + ticker.halt(); + ticker.run(); + + // Kill our Profiler Threads + profiler.kill(); + + // Save all Player Profiles that are still in memory + PlayerProfile.iterator().forEachRemaining(profile -> { + if (profile.isDirty()) { + profile.save(); + } + }); + + // Save all registered Worlds + for (Map.Entry entry : getRegistry().getWorlds().entrySet()) { + try { + entry.getValue().saveAndRemove(); + } catch (Exception x) { + getLogger().log(Level.SEVERE, x, () -> "An Error occurred while saving Slimefun-Blocks in World '" + entry.getKey() + "' for Slimefun " + getVersion()); + } + } + + for (UniversalBlockMenu menu : registry.getUniversalInventories().values()) { + menu.save(); + } + + // Create a new backup zip + backupService.run(); + + // Close and unload any resources from our Metrics Service + metricsService.cleanUp(); + + /** + * Prevent Memory Leaks for reloads... + * These static Maps should really be removed at some point... + */ + AContainer.processing = null; + AContainer.progress = null; + + AGenerator.processing = null; + AGenerator.progress = null; + + Reactor.processing = null; + Reactor.progress = null; + + instance = null; + + /** + * Close all inventories on the server to prevent item dupes + * (Incase some idiot uses /reload) + */ + for (Player p : Bukkit.getOnlinePlayers()) { + p.closeInventory(); + } } @Nonnull @@ -345,8 +444,8 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { getLogger().log(Level.SEVERE, "### Slimefun was not installed correctly!"); getLogger().log(Level.SEVERE, "### You are using the wrong version of Minecraft!"); getLogger().log(Level.SEVERE, "###"); - getLogger().log(Level.SEVERE, "### You are using Minecraft {0}", ReflectionUtils.getVersion()); - getLogger().log(Level.SEVERE, "### but Slimefun v{0} requires you to be using", getDescription().getVersion()); + getLogger().log(Level.SEVERE, "### You are using Minecraft {0}", currentVersion); + getLogger().log(Level.SEVERE, "### but Slimefun {0} requires you to be using", getDescription().getVersion()); getLogger().log(Level.SEVERE, "### Minecraft {0}", String.join(" / ", getSupportedVersions())); getLogger().log(Level.SEVERE, "#############################################"); return true; @@ -361,7 +460,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { List list = new ArrayList<>(); for (MinecraftVersion version : MinecraftVersion.valuesCache) { - if (version != MinecraftVersion.UNKNOWN) { + if (!version.isVirtual()) { list.add(version.getName()); } } @@ -369,65 +468,6 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { return list; } - @Override - public void onDisable() { - // Slimefun never loaded successfully, so we don't even bother doing stuff here - if (instance() == null || minecraftVersion == MinecraftVersion.UNIT_TEST) { - return; - } - - // Cancel all tasks from this plugin immediately - Bukkit.getScheduler().cancelTasks(this); - - // Finishes all started movements/removals of block data - ticker.halt(); - ticker.run(); - - // Save all Player Profiles that are still in memory - PlayerProfile.iterator().forEachRemaining(profile -> { - if (profile.isDirty()) { - profile.save(); - } - }); - - // Save all registered Worlds - for (Map.Entry entry : getRegistry().getWorlds().entrySet()) { - try { - entry.getValue().saveAndRemove(); - } catch (Exception x) { - getLogger().log(Level.SEVERE, x, () -> "An Error occurred while saving Slimefun-Blocks in World '" + entry.getKey() + "' for Slimefun " + getVersion()); - } - } - - for (UniversalBlockMenu menu : registry.getUniversalInventories().values()) { - menu.save(); - } - - // Create a new backup zip - backupService.run(); - - metricsService.cleanUp(); - - // Prevent Memory Leaks - // These static Maps should be removed at some point... - AContainer.processing = null; - AContainer.progress = null; - - AGenerator.processing = null; - AGenerator.progress = null; - - Reactor.processing = null; - Reactor.progress = null; - - instance = null; - - // Close all inventories on the server to prevent item dupes - // (Incase some idiot uses /reload) - for (Player p : Bukkit.getOnlinePlayers()) { - p.closeInventory(); - } - } - private void createDirectories() { String[] storageFolders = { "Players", "blocks", "stored-blocks", "stored-inventories", "stored-chunks", "universal-inventories", "waypoints", "block-backups" }; String[] pluginFolders = { "scripts", "error-reports", "cache/github", "world-settings" }; @@ -449,9 +489,12 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { } } + /** + * This method registers all of our {@link Listener Listeners}. + */ private void registerListeners() { new SlimefunBootsListener(this); - new SlimefunItemListener(this); + new SlimefunItemInteractListener(this); new SlimefunItemConsumeListener(this); new BlockPhysicsListener(this); new CargoNodeListener(this); @@ -461,6 +504,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { new BlockListener(this); new EnhancedFurnaceListener(this); new ItemPickupListener(this); + new ItemDropListener(this); new DeathpointListener(this); new ExplosionsListener(this); new DebugFishListener(this); @@ -470,19 +514,23 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { new EntityInteractionListener(this); new MobDropListener(this); new VillagerTradingListener(this); - new ElytraCrashListener(this); + new ElytraImpactListener(this); new CraftingTableListener(this); new AnvilListener(this); new BrewingStandListener(this); new CauldronListener(this); new GrindstoneListener(this); new CartographyTableListener(this); + new ButcherAndroidListener(this); + new NetworkListener(this, networkManager); + // Bees were added in 1.15 if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_15)) { new BeeListener(this); new BeeWingsListener(this, (BeeWings) SlimefunItems.BEE_WINGS.getItem()); } + // Piglins were added in 1.16 if (minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_16)) { new PiglinListener(this); } @@ -518,7 +566,10 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { private void loadTags() { for (SlimefunTag tag : SlimefunTag.valuesCache) { try { - tag.reload(); + // Only reload "empty" (or unloaded) Tags + if (tag.isEmpty()) { + tag.reload(); + } } catch (TagMisconfigurationException e) { getLogger().log(Level.SEVERE, e, () -> "Failed to load Tag: " + tag.name()); } @@ -723,8 +774,15 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon { return instance.isNewlyInstalled; } + @Nonnull public static String getCSCoreLibVersion() { - return CSCoreLib.getLib().getDescription().getVersion(); + Plugin cscorelib = instance.getServer().getPluginManager().getPlugin("CS-CoreLib"); + + if (cscorelib == null) { + throw new IllegalStateException("CS-CoreLib is not installed."); + } else { + return cscorelib.getDescription().getVersion(); + } } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/BookSlimefunGuide.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/BookSlimefunGuide.java index 26f894046..a5c7c1d29 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/BookSlimefunGuide.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/BookSlimefunGuide.java @@ -215,11 +215,7 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation { ChatComponent component = new ChatComponent(ChatUtils.crop(ChatColor.RED, item.getItemName()) + "\n"); component.setHoverEvent(new HoverEvent(ChatColor.RESET + item.getItemName(), ChatColor.DARK_RED.toString() + ChatColor.BOLD + SlimefunPlugin.getLocalization().getMessage(p, "guide.locked"), "", ChatColor.GREEN + "> Click to unlock", "", ChatColor.GRAY + "Cost: " + ChatColor.AQUA.toString() + research.getCost() + " Level(s)")); - component.setClickEvent(new ClickEvent(key, player -> - SlimefunPlugin.runSync(() -> - research.unlockFromGuide(this, player, profile, item, category, page) - ) - )); + component.setClickEvent(new ClickEvent(key, player -> SlimefunPlugin.runSync(() -> research.unlockFromGuide(this, player, profile, item, category, page)))); items.add(component); } else { 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 428e6e732..c741431f5 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 @@ -496,6 +496,7 @@ public class ProgrammableAndroid extends SlimefunItem implements InventoryBlock, menu.open(p); } + @Nonnull protected List getValidScriptInstructions() { List list = new ArrayList<>(); @@ -593,8 +594,9 @@ public class ProgrammableAndroid extends SlimefunItem implements InventoryBlock, } } - public void registerFuelType(MachineFuel fuel) { + public void registerFuelType(@Nonnull MachineFuel fuel) { Validate.notNull(fuel, "Cannot register null as a Fuel type"); + fuelTypes.add(fuel); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Script.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Script.java index 7ee370638..6437e91f6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Script.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Script.java @@ -151,7 +151,7 @@ public final class Script { @Nonnull private String getScriptRatingPercentage() { float percentage = getRating(); - return NumberUtils.getColorFromPercentage(percentage) + String.valueOf(percentage) + ChatColor.RESET + "% "; + return NumberUtils.getColorFromPercentage(percentage) + String.valueOf(percentage) + ChatColor.WHITE + "% "; } /** 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 384289e8e..5e9df87ac 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 @@ -12,7 +12,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.DamageableItem; import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectionType; import io.github.thebusybiscuit.slimefun4.core.attributes.ProtectiveArmor; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; -import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraCrashListener; +import io.github.thebusybiscuit.slimefun4.implementation.listeners.ElytraImpactListener; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; @@ -22,7 +22,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; * * @author Seggan * - * @see ElytraCrashListener + * @see ElytraImpactListener */ public class ElytraCap extends SlimefunArmorPiece implements DamageableItem, ProtectiveArmor { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/Parachute.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/Parachute.java index f99cbb336..f18535f24 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/Parachute.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/Parachute.java @@ -1,5 +1,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.armor; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.Jetpack; @@ -24,6 +26,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; */ public class Parachute extends SlimefunItem { + @ParametersAreNonnullByDefault public Parachute(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, RecipeType.ENHANCED_CRAFTING_TABLE, recipe); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/StomperBoots.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/StomperBoots.java index ca23601bc..8438d7950 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/StomperBoots.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/armor/StomperBoots.java @@ -52,7 +52,7 @@ public class StomperBoots extends SlimefunItem { n.setVelocity(velocity); // Check if it's not a Player or if PvP is enabled - if (!(n instanceof Player) || (p.getWorld().getPVP() && SlimefunPlugin.getProtectionManager().hasPermission(p, n.getLocation(), ProtectableAction.PVP))) { + if (!(n instanceof Player) || (p.getWorld().getPVP() && SlimefunPlugin.getProtectionManager().hasPermission(p, n.getLocation(), ProtectableAction.ATTACK_PLAYER))) { EntityDamageByEntityEvent event = new EntityDamageByEntityEvent(p, n, DamageCause.ENTITY_ATTACK, fallDamageEvent.getDamage() / 2); Bukkit.getPluginManager().callEvent(event); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/AbstractMonsterSpawner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/AbstractMonsterSpawner.java new file mode 100644 index 000000000..2026ff95a --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/AbstractMonsterSpawner.java @@ -0,0 +1,113 @@ +package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; + +import java.util.List; +import java.util.Locale; +import java.util.Optional; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.apache.commons.lang.Validate; +import org.bukkit.ChatColor; +import org.bukkit.block.BlockState; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.bukkit.inventory.meta.ItemMeta; + +import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; +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; + +/** + * This is a parent class for the {@link BrokenSpawner} and {@link RepairedSpawner} + * to provide some utility methods. + * + * @author TheBusyBiscuit + * + * @see BrokenSpawner + * @see RepairedSpawner + * + */ +public abstract class AbstractMonsterSpawner extends SlimefunItem { + + @ParametersAreNonnullByDefault + AbstractMonsterSpawner(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { + super(category, item, recipeType, recipe); + } + + /** + * This method tries to obtain an {@link EntityType} from a given {@link ItemStack}. + * The provided {@link ItemStack} must be a {@link RepairedSpawner} item. + * + * @param item + * The {@link ItemStack} to extract the {@link EntityType} from + * + * @return An {@link Optional} describing the result + */ + @Nonnull + public Optional getEntityType(@Nonnull ItemStack item) { + Validate.notNull(item, "The Item cannot be null"); + + ItemMeta meta = item.getItemMeta(); + + // We may want to update this in the future to also make use of the BlockStateMeta + for (String line : meta.getLore()) { + if (ChatColor.stripColor(line).startsWith("Type: ") && !line.contains("")) { + EntityType type = EntityType.valueOf(ChatColor.stripColor(line).replace("Type: ", "").replace(' ', '_').toUpperCase(Locale.ROOT)); + return Optional.of(type); + } + } + + return Optional.empty(); + } + + /** + * This method returns a finished {@link ItemStack} of this {@link SlimefunItem}, modified + * to hold and represent the given {@link EntityType}. + * It updates the lore and {@link BlockStateMeta} to reflect the specified {@link EntityType}. + * + * @param type + * The {@link EntityType} to apply + * + * @return An {@link ItemStack} for this {@link SlimefunItem} holding that {@link EntityType} + */ + @Nonnull + public ItemStack getItemForEntityType(@Nonnull EntityType type) { + Validate.notNull(type, "The EntityType cannot be null"); + + ItemStack item = getItem().clone(); + ItemMeta meta = item.getItemMeta(); + + // Fixes #2583 - Proper NBT handling of Spawners + if (meta instanceof BlockStateMeta) { + BlockStateMeta stateMeta = (BlockStateMeta) meta; + BlockState state = stateMeta.getBlockState(); + + if (state instanceof CreatureSpawner) { + ((CreatureSpawner) state).setSpawnedType(type); + } + + stateMeta.setBlockState(state); + } + + // Setting the lore to indicate the Type visually + List lore = meta.getLore(); + + for (int i = 0; i < lore.size(); i++) { + if (lore.get(i).contains("")) { + lore.set(i, lore.get(i).replace("", ChatUtils.humanize(type.name()))); + break; + } + } + + meta.setLore(lore); + item.setItemMeta(meta); + + return item; + } + +} 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 810cdf699..f5800321d 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 @@ -79,21 +79,25 @@ public class BlockPlacer extends SlimefunItem { Material material = e.getItem().getType(); if (SlimefunTag.SHULKER_BOXES.isTagged(material)) { - // Since vanilla Dispensers can already place Shulker boxes, we - // simply fallback to the vanilla behaviour. + /** + * Since vanilla Dispensers can already place Shulker boxes, + * we simply fallback to the vanilla behaviour. + */ return; } 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. + /** + * 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() && !isBlacklisted(material)) { + if (facedBlock.isEmpty() && !isBlacklisted(material) && dispenser.getInventory().getViewers().isEmpty()) { SlimefunItem item = SlimefunItem.getByItem(e.getItem()); if (item != null) { @@ -123,9 +127,11 @@ public class BlockPlacer extends SlimefunItem { String owner = BlockStorage.getLocationInfo(dispenser.getLocation(), "owner"); if (owner == null) { - // If no owner was set, then we will fallback to the previous behaviour: - // Allowing block placers to bypass protection, newly placed Block placers - // will respect protection plugins. + /** + * If no owner was set, then we will fallback to the previous behaviour: + * Allowing block placers to bypass protection, newly placed Block placers + * will respect protection plugins. + */ return true; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BrokenSpawner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BrokenSpawner.java index db74d0be3..6f997bfda 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BrokenSpawner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/BrokenSpawner.java @@ -1,7 +1,13 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.inventory.ItemStack; +import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent; +import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; +import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; @@ -17,10 +23,18 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; * @see RepairedSpawner * */ -public class BrokenSpawner extends UnplaceableBlock { +public class BrokenSpawner extends AbstractMonsterSpawner implements NotPlaceable { + @ParametersAreNonnullByDefault public BrokenSpawner(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); + + addItemHandler(onRightClick()); + } + + @Nonnull + private ItemUseHandler onRightClick() { + return PlayerRightClickEvent::cancel; } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Composter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Composter.java index a884c8733..183eb3fc3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Composter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Composter.java @@ -83,7 +83,7 @@ public class Composter extends SimpleSlimefunItem implements Re Player p = e.getPlayer(); Block b = block.get(); - if (p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES)) { + if (p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK)) { ItemStack input = e.getItem(); ItemStack output = getOutput(p, input); 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 73cd1902f..9f84d8bf9 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 @@ -95,7 +95,7 @@ public class Crucible extends SimpleSlimefunItem implements Rec Player p = e.getPlayer(); Block b = optional.get(); - if (p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES)) { + if (p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK)) { ItemStack input = e.getItem(); Block block = b.getRelative(BlockFace.UP); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/RepairedSpawner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/RepairedSpawner.java index 003299059..dbfe55178 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/RepairedSpawner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/RepairedSpawner.java @@ -2,20 +2,21 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; import java.util.ArrayList; import java.util.Collection; -import java.util.Locale; -import java.util.Optional; -import org.bukkit.ChatColor; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Material; import org.bukkit.block.CreatureSpawner; -import org.bukkit.entity.EntityType; import org.bukkit.event.block.BlockEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.api.events.BlockPlacerPlaceEvent; +import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; -import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; +import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler; +import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; @@ -28,14 +29,32 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; * @see BrokenSpawner * */ -public class RepairedSpawner extends SimpleSlimefunItem { +public class RepairedSpawner extends AbstractMonsterSpawner { + private final ItemSetting allowSpawnEggs = new ItemSetting<>("allow-spawn-eggs", true); + + @ParametersAreNonnullByDefault public RepairedSpawner(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); + + addItemSetting(allowSpawnEggs); + + addItemHandler(onInteract()); + addItemHandler(onPlace()); } - @Override - public BlockPlaceHandler getItemHandler() { + @Nonnull + private BlockUseHandler onInteract() { + return e -> { + if (!allowSpawnEggs.getValue() && SlimefunTag.SPAWN_EGGS.isTagged(e.getItem().getType())) { + // Disallow spawn eggs from being used on Reinforced Spawners if disabled + e.cancel(); + } + }; + } + + @Nonnull + private BlockPlaceHandler onPlace() { return new BlockPlaceHandler(true) { @Override @@ -48,42 +67,29 @@ public class RepairedSpawner extends SimpleSlimefunItem { onPlace(e.getItemStack(), e); } + @ParametersAreNonnullByDefault private void onPlace(ItemStack item, BlockEvent e) { - Optional entity = getEntityType(item); - - if (entity.isPresent() && e.getBlock().getType() == Material.SPAWNER) { - CreatureSpawner spawner = (CreatureSpawner) e.getBlock().getState(); - spawner.setSpawnedType(entity.get()); - spawner.update(true, false); + /** + * This may no longer be needed at some point but for legacy items + * we still need to set the spawned EntityType manually + */ + if (e.getBlock().getType() == Material.SPAWNER) { + getEntityType(item).ifPresent(entity -> { + CreatureSpawner spawner = (CreatureSpawner) e.getBlock().getState(); + spawner.setSpawnedType(entity); + spawner.update(true, false); + }); } } }; } - /** - * This method tries to obtain an {@link EntityType} from a given {@link ItemStack}. - * The provided {@link ItemStack} must be a {@link RepairedSpawner} item. - * - * @param item - * The {@link ItemStack} to extract the {@link EntityType} from - * - * @return An {@link Optional} describing the result - */ - public Optional getEntityType(ItemStack item) { - for (String line : item.getItemMeta().getLore()) { - if (ChatColor.stripColor(line).startsWith("Type: ") && !line.contains("")) { - EntityType type = EntityType.valueOf(ChatColor.stripColor(line).replace("Type: ", "").replace(' ', '_').toUpperCase(Locale.ROOT)); - return Optional.of(type); - } - } - - return Optional.empty(); - } - @Override public Collection getDrops() { - // There should be no drops by default since drops are handled by the - // Pickaxe of Containment exclusively. + /** + * There should be no drops by default since drops are handled + * by the Pickaxe of Containment exclusively. + */ return new ArrayList<>(); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/package-info.java index c9fec4651..3e0fdcb39 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/package-info.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/package-info.java @@ -1,7 +1,6 @@ /** - * This package contains different implementations of + * This package contains a few miscellaneous implementations of * {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem} which are blocks. - * Such as the {@link io.github.thebusybiscuit.slimefun4.implementation.items.blocks.InfusedHopper} or the - * {@link io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BlockPlacer} for example. + * Such as the {@link io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BlockPlacer} for example. */ package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/AbstractCargoNode.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/AbstractCargoNode.java index 723ad0df3..58a9fd1a0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/AbstractCargoNode.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/AbstractCargoNode.java @@ -1,5 +1,9 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.cargo; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockPlaceEvent; @@ -9,6 +13,7 @@ import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import io.github.thebusybiscuit.slimefun4.utils.ColoredMaterial; @@ -16,7 +21,6 @@ import io.github.thebusybiscuit.slimefun4.utils.HeadTexture; import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; -import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; @@ -29,14 +33,42 @@ import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; * @author TheBusyBiscuit * */ -abstract class AbstractCargoNode extends SlimefunItem { +abstract class AbstractCargoNode extends SimpleSlimefunItem { protected static final String FREQUENCY = "frequency"; + @ParametersAreNonnullByDefault public AbstractCargoNode(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) { super(category, item, recipeType, recipe, recipeOutput); - addItemHandler(new BlockPlaceHandler(false) { + new BlockMenuPreset(getId(), ChatUtils.removeColorCodes(item.getItemMeta().getDisplayName())) { + + @Override + public void init() { + createBorder(this); + } + + @Override + public void newInstance(BlockMenu menu, Block b) { + menu.addMenuCloseHandler(p -> markDirty(b.getLocation())); + updateBlockMenu(menu, b); + } + + @Override + public boolean canOpen(Block b, Player p) { + return p.hasPermission("slimefun.cargo.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK); + } + + @Override + public int[] getSlotsAccessedByItemTransport(ItemTransportFlow flow) { + return new int[0]; + } + }; + } + + @Override + public BlockPlaceHandler getItemHandler() { + return new BlockPlaceHandler(false) { @Override public void onPlayerPlace(BlockPlaceEvent e) { @@ -49,32 +81,10 @@ abstract class AbstractCargoNode extends SlimefunItem { onPlace(e); } - }); - - new BlockMenuPreset(getId(), ChatUtils.removeColorCodes(item.getItemMeta().getDisplayName())) { - - @Override - public void init() { - createBorder(this); - } - - @Override - public void newInstance(BlockMenu menu, Block b) { - updateBlockMenu(menu, b); - } - - @Override - public boolean canOpen(Block b, Player p) { - return p.hasPermission("slimefun.cargo.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES); - } - - @Override - public int[] getSlotsAccessedByItemTransport(ItemTransportFlow flow) { - return new int[0]; - } }; } + @ParametersAreNonnullByDefault protected void addChannelSelector(Block b, BlockMenu menu, int slotPrev, int slotCurrent, int slotNext) { boolean isChestTerminalInstalled = SlimefunPlugin.getThirdPartySupportService().isChestTerminalInstalled(); int channel = getSelectedChannel(b); @@ -122,7 +132,7 @@ abstract class AbstractCargoNode extends SlimefunItem { }); } - private int getSelectedChannel(Block b) { + private int getSelectedChannel(@Nonnull Block b) { if (!BlockStorage.hasBlockInfo(b)) { return 0; } else { @@ -137,10 +147,12 @@ abstract class AbstractCargoNode extends SlimefunItem { } } - protected abstract void onPlace(BlockPlaceEvent e); + protected abstract void onPlace(@Nonnull BlockPlaceEvent e); - protected abstract void createBorder(BlockMenuPreset preset); + protected abstract void createBorder(@Nonnull BlockMenuPreset preset); - protected abstract void updateBlockMenu(BlockMenu menu, Block b); + protected abstract void updateBlockMenu(@Nonnull BlockMenu menu, @Nonnull Block b); + + protected abstract void markDirty(@Nonnull Location loc); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/AbstractFilterNode.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/AbstractFilterNode.java index 2a45af496..bf486f1b9 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/AbstractFilterNode.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/AbstractFilterNode.java @@ -1,11 +1,15 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.cargo; +import javax.annotation.Nonnull; + +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.cscorelib2.item.CustomItem; +import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; @@ -65,7 +69,8 @@ abstract class AbstractFilterNode extends AbstractCargoNode { @Override protected void updateBlockMenu(BlockMenu menu, Block b) { - String filterType = BlockStorage.getLocationInfo(b.getLocation(), FILTER_TYPE); + Location loc = b.getLocation(); + String filterType = BlockStorage.getLocationInfo(loc, FILTER_TYPE); if (!BlockStorage.hasBlockInfo(b) || filterType == null || filterType.equals("whitelist")) { menu.replaceExistingItem(15, new CustomItem(Material.WHITE_WOOL, "&7Type: &rWhitelist", "", "&e> Click to change it to Blacklist")); @@ -102,6 +107,16 @@ abstract class AbstractFilterNode extends AbstractCargoNode { } addChannelSelector(b, menu, 41, 42, 43); + markDirty(loc); + } + + @Override + protected void markDirty(@Nonnull Location loc) { + CargoNet network = CargoNet.getNetworkFromLocation(loc); + + if (network != null) { + network.markCargoNodeConfigurationDirty(loc); + } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoOutputNode.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoOutputNode.java index 31ea4dcd8..6d33b69e2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoOutputNode.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoOutputNode.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.cargo; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.event.block.BlockPlaceEvent; @@ -38,4 +39,9 @@ public class CargoOutputNode extends AbstractCargoNode { addChannelSelector(b, menu, 12, 13, 14); } + @Override + protected void markDirty(Location loc) { + // No need to mark anything as dirty, there is no item filter. + } + } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/ReactorAccessPort.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/ReactorAccessPort.java index 05791763c..8f04493dd 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/ReactorAccessPort.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/ReactorAccessPort.java @@ -46,7 +46,7 @@ public class ReactorAccessPort extends SlimefunItem { @Override public boolean canOpen(Block b, Player p) { - return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES); + return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK); } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/AbstractEnergyProvider.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/AbstractEnergyProvider.java index b7767796b..3a985b381 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/AbstractEnergyProvider.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/AbstractEnergyProvider.java @@ -85,7 +85,7 @@ public abstract class AbstractEnergyProvider extends SlimefunItem implements Inv @Override @Nonnull - public EnergyNetComponentType getEnergyComponentType() { + public final EnergyNetComponentType getEnergyComponentType() { return EnergyNetComponentType.GENERATOR; } @@ -95,7 +95,7 @@ public abstract class AbstractEnergyProvider extends SlimefunItem implements Inv } @Nonnull - public Set getFuelTypes2() { + public Set getFuelTypes() { return fuelTypes; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/Capacitor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/Capacitor.java index 96049b9c0..aea469f27 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/Capacitor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/Capacitor.java @@ -38,7 +38,7 @@ public class Capacitor extends SlimefunItem implements EnergyNetComponent { @Override @Nonnull - public EnergyNetComponentType getEnergyComponentType() { + public final EnergyNetComponentType getEnergyComponentType() { return EnergyNetComponentType.CAPACITOR; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyConnector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyConnector.java index ca93c9482..248b87c67 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyConnector.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/EnergyConnector.java @@ -53,7 +53,7 @@ public class EnergyConnector extends SimpleSlimefunItem impleme @Nonnull @Override - public EnergyNetComponentType getEnergyComponentType() { + public final EnergyNetComponentType getEnergyComponentType() { return EnergyNetComponentType.CONNECTOR; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/gadgets/Multimeter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/gadgets/Multimeter.java index 77c91ce0d..5fa0bb688 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/gadgets/Multimeter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/gadgets/Multimeter.java @@ -8,12 +8,12 @@ import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNet; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; +import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; @@ -50,8 +50,8 @@ public class Multimeter extends SimpleSlimefunItem { e.cancel(); Location l = e.getClickedBlock().get().getLocation(); - String stored = DoubleHandler.getFancyDouble(component.getCharge(l)) + " J"; - String capacity = DoubleHandler.getFancyDouble(component.getCapacity()) + " J"; + String stored = NumberUtils.getCompactDouble(component.getCharge(l)) + " J"; + String capacity = NumberUtils.getCompactDouble(component.getCapacity()) + " J"; Player p = e.getPlayer(); p.sendMessage(""); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/SolarGenerator.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/SolarGenerator.java index 942945155..2b67bcd1f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/SolarGenerator.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/generators/SolarGenerator.java @@ -61,10 +61,15 @@ public class SolarGenerator extends SlimefunItem implements EnergyNetProvider { } @Override - public int getCapacity() { + public final int getCapacity() { return 0; } + @Override + public final boolean isChargeable() { + return false; + } + @Override public int getGeneratedOutput(Location l, Config data) { World world = l.getWorld(); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AbstractEntityAssembler.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AbstractEntityAssembler.java index e8d5b962f..b6eb0c527 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AbstractEntityAssembler.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AbstractEntityAssembler.java @@ -80,7 +80,7 @@ public abstract class AbstractEntityAssembler extends SimpleSl @Override public boolean canOpen(Block b, Player p) { - return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES); + return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK); } @Override @@ -183,7 +183,7 @@ public abstract class AbstractEntityAssembler extends SimpleSl return; } - if (lifetime % 60 == 0 && getCharge(b.getLocation()) >= getEnergyConsumption()) { + if (lifetime % 60 == 0 && getCharge(b.getLocation(), data) >= getEnergyConsumption()) { BlockMenu menu = BlockStorage.getInventory(b); boolean hasBody = findResource(menu, getBody(), bodySlots); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AbstractGrowthAccelerator.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AbstractGrowthAccelerator.java index 2b08fa441..065f57e1c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AbstractGrowthAccelerator.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AbstractGrowthAccelerator.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.block.Block; import org.bukkit.inventory.ItemStack; @@ -23,6 +25,7 @@ public abstract class AbstractGrowthAccelerator extends SlimefunItem implements private static final int[] BORDER = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }; + @ParametersAreNonnullByDefault public AbstractGrowthAccelerator(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/AutoDisenchanter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoDisenchanter.java index 87183a6e3..45ca141b7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoDisenchanter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoDisenchanter.java @@ -1,11 +1,10 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -17,9 +16,6 @@ import org.bukkit.inventory.meta.Repairable; import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils; import io.github.thebusybiscuit.slimefun4.api.events.AutoDisenchantEvent; -import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; -import me.mrCookieSlime.EmeraldEnchants.EmeraldEnchants; -import me.mrCookieSlime.EmeraldEnchants.ItemEnchantment; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; @@ -42,6 +38,7 @@ import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; */ public class AutoDisenchanter extends AContainer { + @ParametersAreNonnullByDefault public AutoDisenchanter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); } @@ -54,7 +51,6 @@ public class AutoDisenchanter extends AContainer { @Override protected MachineRecipe findNextRecipe(BlockMenu menu) { Map enchantments = new HashMap<>(); - Set emeraldEnchantments = new HashSet<>(); for (int slot : getInputSlots()) { ItemStack item = menu.getItemInSlot(slot); @@ -81,13 +77,6 @@ public class AutoDisenchanter extends AContainer { amount++; } - if (SlimefunPlugin.getThirdPartySupportService().isEmeraldEnchantsInstalled()) { - for (ItemEnchantment enchantment : EmeraldEnchants.getInstance().getRegistry().getEnchantments(item)) { - amount++; - emeraldEnchantments.add(enchantment); - } - } - if (amount > 0) { ItemStack disenchantedItem = item.clone(); disenchantedItem.setAmount(1); @@ -95,11 +84,6 @@ public class AutoDisenchanter extends AContainer { ItemStack book = new ItemStack(Material.ENCHANTED_BOOK); transferEnchantments(disenchantedItem, book, enchantments); - for (ItemEnchantment ench : emeraldEnchantments) { - EmeraldEnchants.getInstance().getRegistry().applyEnchantment(book, ench.getEnchantment(), ench.getLevel()); - EmeraldEnchants.getInstance().getRegistry().applyEnchantment(disenchantedItem, ench.getEnchantment(), 0); - } - MachineRecipe recipe = new MachineRecipe(90 * amount / this.getSpeed(), new ItemStack[] { target, item }, new ItemStack[] { disenchantedItem, book }); if (!InvUtils.fitAll(menu.toInventory(), recipe.getOutput(), getOutputSlots())) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoEnchanter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoEnchanter.java index e72dd1e6b..e1550792e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoEnchanter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoEnchanter.java @@ -1,9 +1,9 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; + +import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; @@ -11,9 +11,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.EnchantmentStorageMeta; import io.github.thebusybiscuit.cscorelib2.inventory.InvUtils; -import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; -import me.mrCookieSlime.EmeraldEnchants.EmeraldEnchants; -import me.mrCookieSlime.EmeraldEnchants.ItemEnchantment; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; @@ -24,12 +21,9 @@ import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; public class AutoEnchanter extends AContainer { - private final int emeraldEnchantsLimit; - + @ParametersAreNonnullByDefault public AutoEnchanter(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); - - emeraldEnchantsLimit = SlimefunPlugin.getCfg().getInt("options.emerald-enchantment-limit"); } @Override @@ -51,9 +45,7 @@ public class AutoEnchanter extends AContainer { if (item != null && item.getType() == Material.ENCHANTED_BOOK && target != null) { Map enchantments = new HashMap<>(); - Set emeraldEnchantments = new HashSet<>(); int amount = 0; - int specialAmount = 0; EnchantmentStorageMeta meta = (EnchantmentStorageMeta) item.getItemMeta(); for (Map.Entry e : meta.getStoredEnchants().entrySet()) { @@ -63,19 +55,7 @@ public class AutoEnchanter extends AContainer { } } - if (SlimefunPlugin.getThirdPartySupportService().isEmeraldEnchantsInstalled()) { - for (ItemEnchantment enchantment : EmeraldEnchants.getInstance().getRegistry().getEnchantments(item)) { - if (EmeraldEnchants.getInstance().getRegistry().isApplicable(target, enchantment.getEnchantment()) && EmeraldEnchants.getInstance().getRegistry().getEnchantmentLevel(target, enchantment.getEnchantment().getName()) < enchantment.getLevel()) { - amount++; - specialAmount++; - emeraldEnchantments.add(enchantment); - } - } - - specialAmount += EmeraldEnchants.getInstance().getRegistry().getEnchantments(target).size(); - } - - if (amount > 0 && specialAmount <= emeraldEnchantsLimit) { + if (amount > 0) { ItemStack enchantedItem = target.clone(); enchantedItem.setAmount(1); @@ -83,10 +63,6 @@ public class AutoEnchanter extends AContainer { enchantedItem.addUnsafeEnchantment(entry.getKey(), entry.getValue()); } - for (ItemEnchantment ench : emeraldEnchantments) { - EmeraldEnchants.getInstance().getRegistry().applyEnchantment(enchantedItem, ench.getEnchantment(), ench.getLevel()); - } - MachineRecipe recipe = new MachineRecipe(75 * amount / this.getSpeed(), new ItemStack[] { target, item }, new ItemStack[] { enchantedItem, new ItemStack(Material.BOOK) }); if (!InvUtils.fitAll(menu.toInventory(), recipe.getOutput(), getOutputSlots())) { 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 268736c13..93031d45c 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,7 +3,9 @@ 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 org.bukkit.Material; import org.bukkit.block.Block; @@ -18,7 +20,9 @@ import io.github.thebusybiscuit.slimefun4.api.events.BlockPlacerPlaceEvent; 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 io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.EnhancedCraftingTable; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction; @@ -40,6 +44,8 @@ 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. * + * @deprecated This is horribly done. Someone needs to rewrite this. + * * @author TheBusyBiscuit * */ @@ -49,6 +55,8 @@ 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<>(); + public AutomatedCraftingChamber(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); @@ -86,7 +94,7 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I @Override public boolean canOpen(Block b, Player p) { - return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES); + return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK); } @Override @@ -267,7 +275,7 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I private void testInputAgainstRecipes(Block block, String input) { BlockMenu menu = BlockStorage.getInventory(block); - ItemStack output = SlimefunPlugin.getRegistry().getAutomatedCraftingChamberRecipes().get(input); + ItemStack output = craftingRecipes.get(input); if (output != null && menu.fits(output, getOutputSlots())) { menu.pushItem(output.clone(), getOutputSlots()); removeCharge(block.getLocation(), getEnergyConsumption()); @@ -279,4 +287,25 @@ public abstract class AutomatedCraftingChamber extends SlimefunItem implements I } } } + + 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)); + } + } } 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 eac1bcea2..403085a55 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 @@ -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; @@ -11,8 +13,16 @@ import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; +/** + * The {@link ElectricPress} is a pretty simple electrical machine. + * It allows you to compact items into their block variant, e.g. 9 diamonds into a diamond block. + * + * @author TheBusyBiscuit + * + */ public class ElectricPress extends AContainer implements RecipeDisplayItem { + @ParametersAreNonnullByDefault public ElectricPress(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); } @@ -28,6 +38,7 @@ public class ElectricPress extends AContainer implements RecipeDisplayItem { 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)); addRecipe(3, new ItemStack(Material.CLAY_BALL, 4), new ItemStack(Material.CLAY)); + addRecipe(3, new ItemStack(Material.BRICK, 4), new ItemStack(Material.BRICKS)); addRecipe(6, SlimefunItems.COPPER_INGOT, new CustomItem(SlimefunItems.COPPER_WIRE, 3)); addRecipe(16, new SlimefunItemStack(SlimefunItems.STEEL_INGOT, 8), SlimefunItems.STEEL_PLATE); @@ -61,6 +72,7 @@ public class ElectricPress extends AContainer implements RecipeDisplayItem { addRecipe(8, new ItemStack(Material.DIAMOND, 9), new ItemStack(Material.DIAMOND_BLOCK)); } + @ParametersAreNonnullByDefault private void addRecipe(int seconds, ItemStack input, ItemStack output) { registerRecipe(seconds, new ItemStack[] { input }, new ItemStack[] { output }); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricSmeltery.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricSmeltery.java index 4848b0514..0768c6621 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricSmeltery.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/ElectricSmeltery.java @@ -53,7 +53,7 @@ public class ElectricSmeltery extends AContainer { @Override public boolean canOpen(Block b, Player p) { - return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES); + return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK); } @Override 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 d5a2168ee..1190831ae 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 @@ -2,6 +2,10 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.electric.machine import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; @@ -13,12 +17,12 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.cscorelib2.blocks.Vein; -import io.github.thebusybiscuit.cscorelib2.item.CustomItem; import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent; import io.github.thebusybiscuit.slimefun4.core.networks.energy.EnergyNetComponentType; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; +import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.AdvancedMenuClickHandler; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ClickAction; @@ -49,6 +53,9 @@ public class FluidPump extends SimpleSlimefunItem implements Invent private final int[] inputBorder = { 9, 10, 11, 12, 18, 21, 27, 28, 29, 30 }; private final int[] outputBorder = { 14, 15, 16, 17, 23, 26, 32, 33, 34, 35 }; + private final ItemStack emptyBucket = new ItemStackWrapper(Material.BUCKET); + + @ParametersAreNonnullByDefault public FluidPump(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); @@ -66,16 +73,16 @@ public class FluidPump extends SimpleSlimefunItem implements Invent }); } - private void constructMenu(BlockMenuPreset preset) { + private void constructMenu(@Nonnull BlockMenuPreset preset) { for (int i : border) { - preset.addItem(i, new CustomItem(Material.GRAY_STAINED_GLASS_PANE, " "), ChestMenuUtils.getEmptyClickHandler()); + preset.addItem(i, ChestMenuUtils.getBackground(), ChestMenuUtils.getEmptyClickHandler()); } for (int i : inputBorder) { - preset.addItem(i, new CustomItem(Material.CYAN_STAINED_GLASS_PANE, " "), ChestMenuUtils.getEmptyClickHandler()); + preset.addItem(i, ChestMenuUtils.getInputSlotTexture(), ChestMenuUtils.getEmptyClickHandler()); } for (int i : outputBorder) { - preset.addItem(i, new CustomItem(Material.ORANGE_STAINED_GLASS_PANE, " "), ChestMenuUtils.getEmptyClickHandler()); + preset.addItem(i, ChestMenuUtils.getOutputSlotTexture(), ChestMenuUtils.getEmptyClickHandler()); } for (int i : getOutputSlots()) { @@ -114,14 +121,14 @@ public class FluidPump extends SimpleSlimefunItem implements Invent return 512; } - protected void tick(Block b) { + protected void tick(@Nonnull Block b) { Block fluid = b.getRelative(BlockFace.DOWN); if (fluid.isLiquid() && getCharge(b.getLocation()) >= ENERGY_CONSUMPTION) { BlockMenu menu = BlockStorage.getInventory(b); for (int slot : getInputSlots()) { - if (SlimefunUtils.isItemSimilar(menu.getItemInSlot(slot), new ItemStack(Material.BUCKET), true, false)) { + if (SlimefunUtils.isItemSimilar(menu.getItemInSlot(slot), emptyBucket, true, false)) { ItemStack bucket = getFilledBucket(fluid); if (!menu.fits(bucket, getOutputSlots())) { @@ -143,11 +150,14 @@ public class FluidPump extends SimpleSlimefunItem implements Invent } } - private Block findNextFluid(Block fluid) { + @Nullable + private Block findNextFluid(@Nonnull Block fluid) { if (fluid.getType() == Material.WATER || fluid.getType() == Material.BUBBLE_COLUMN) { - // With water we can be sure to find an infinite source whenever we go - // further than a block, so we can just remove the water here and save - // ourselves all of that computing... + /** + * With water we can be sure to find an infinite source whenever we + * go further than a block, so we can just remove the water here and + * save ourselves all of that computing... + */ if (isSource(fluid)) { return fluid; } @@ -162,10 +172,12 @@ public class FluidPump extends SimpleSlimefunItem implements Invent } } } + return null; } - private ItemStack getFilledBucket(Block fluid) { + @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) { @@ -184,7 +196,7 @@ public class FluidPump extends SimpleSlimefunItem implements Invent * * @return Whether that {@link Block} is a liquid and a source {@link Block}. */ - private boolean isSource(Block block) { + private boolean isSource(@Nonnull Block block) { if (block.isLiquid()) { BlockData data = block.getBlockData(); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/HeatedPressureChamber.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/HeatedPressureChamber.java index d013afb02..95b62ac1f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/HeatedPressureChamber.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/HeatedPressureChamber.java @@ -37,7 +37,7 @@ public class HeatedPressureChamber extends AContainer { @Override public boolean canOpen(Block b, Player p) { - return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES); + return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK); } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/Reactor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/Reactor.java index 010cee0ad..46aa72c8f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/Reactor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/Reactor.java @@ -98,7 +98,7 @@ public abstract class Reactor extends AbstractEnergyProvider { @Override public boolean canOpen(Block b, Player p) { - return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES); + return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK); } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOScanner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOScanner.java index 63e61042b..1c9062a55 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOScanner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOScanner.java @@ -45,6 +45,6 @@ public class GEOScanner extends SimpleSlimefunItem { @ParametersAreNonnullByDefault private boolean hasAccess(Player p, Location l) { - return p.hasPermission("slimefun.gps.bypass") || (SlimefunPlugin.getProtectionManager().hasPermission(p, l, ProtectableAction.ACCESS_INVENTORIES)); + return p.hasPermission("slimefun.gps.bypass") || (SlimefunPlugin.getProtectionManager().hasPermission(p, l, ProtectableAction.INTERACT_BLOCK)); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/OilPump.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/OilPump.java index fb4bfe1c0..9520b89ee 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/OilPump.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/OilPump.java @@ -43,7 +43,7 @@ public class OilPump extends AContainer implements RecipeDisplayItem { @Override public boolean canOpen(Block b, Player p) { - if (!(p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES))) { + if (!(p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK))) { return false; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/ElevatorPlate.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/ElevatorPlate.java index aeffe8f77..1d7dc7ae0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/ElevatorPlate.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/ElevatorPlate.java @@ -36,34 +36,47 @@ import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; +/** + * The {@link ElevatorPlate} is a quick way of teleportation. + * You can place multiple {@link ElevatorPlate ElevatorPlates} along the y axis + * to teleport between them. + * + * @author TheBusyBiscuit + * + */ public class ElevatorPlate extends SimpleSlimefunItem { + /** + * This is our key for storing the floor name. + */ private static final String DATA_KEY = "floor"; + + /** + * This is our {@link Set} of currently teleporting {@link Player Players}. + * It is used to prevent them from triggering the {@link ElevatorPlate} they land on. + */ private final Set users = new HashSet<>(); + @ParametersAreNonnullByDefault public ElevatorPlate(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) { super(category, item, recipeType, recipe, recipeOutput); addItemHandler(onPlace()); } + @Nonnull private BlockPlaceHandler onPlace() { return new BlockPlaceHandler(false) { @Override public void onPlayerPlace(BlockPlaceEvent e) { Block b = e.getBlock(); - BlockStorage.addBlockInfo(b, DATA_KEY, "&rFloor #0"); + BlockStorage.addBlockInfo(b, DATA_KEY, ChatColor.WHITE + "Floor #0"); BlockStorage.addBlockInfo(b, "owner", e.getPlayer().getUniqueId().toString()); } }; } - @Nonnull - public Set getUsers() { - return users; - } - @Override public BlockUseHandler getItemHandler() { return e -> { @@ -172,7 +185,7 @@ public class ElevatorPlate extends SimpleSlimefunItem { public void openEditor(Player p, Block b) { ChestMenu menu = new ChestMenu("Elevator Settings"); - menu.addItem(4, new CustomItem(Material.NAME_TAG, "&7Floor Name &e(Click to edit)", "", "&r" + ChatColors.color(BlockStorage.getLocationInfo(b.getLocation(), DATA_KEY)))); + menu.addItem(4, new CustomItem(Material.NAME_TAG, "&7Floor Name &e(Click to edit)", "", ChatColor.WHITE + ChatColors.color(BlockStorage.getLocationInfo(b.getLocation(), DATA_KEY)))); menu.addMenuClickHandler(4, (pl, slot, item, action) -> { pl.closeInventory(); pl.sendMessage(""); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSControlPanel.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSControlPanel.java index 27d771eba..c1da15310 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSControlPanel.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSControlPanel.java @@ -43,6 +43,6 @@ public class GPSControlPanel extends SimpleSlimefunItem { @ParametersAreNonnullByDefault private boolean hasAccess(Player p, Location l) { - return p.hasPermission("slimefun.gps.bypass") || (SlimefunPlugin.getProtectionManager().hasPermission(p, l, ProtectableAction.ACCESS_INVENTORIES)); + return p.hasPermission("slimefun.gps.bypass") || (SlimefunPlugin.getProtectionManager().hasPermission(p, l, ProtectableAction.INTERACT_BLOCK)); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java index 30d03c851..ecdf45d34 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java @@ -60,7 +60,7 @@ public abstract class GPSTransmitter extends SimpleSlimefunItem imp @Override public void tick(Block b, SlimefunItem item, Config data) { - int charge = getCharge(b.getLocation()); + int charge = getCharge(b.getLocation(), data); UUID owner = UUID.fromString(BlockStorage.getLocationInfo(b.getLocation(), "owner")); if (charge >= getEnergyConsumption()) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/InfusedHopper.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/InfusedHopper.java similarity index 65% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/InfusedHopper.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/InfusedHopper.java index 26f776093..84f69890a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/InfusedHopper.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/InfusedHopper.java @@ -1,10 +1,14 @@ -package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; +package io.github.thebusybiscuit.slimefun4.implementation.items.magical; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.SoundCategory; import org.bukkit.block.Block; +import org.bukkit.block.data.type.Hopper; import org.bukkit.entity.Entity; import org.bukkit.entity.Item; import org.bukkit.inventory.ItemStack; @@ -22,15 +26,27 @@ import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; +/** + * The {@link InfusedHopper} is a special kind of {@link Hopper} which teleports any + * neaby {@link Item} to itself. + * The radius can be configured in the config. + * + * @author TheBusyBiscuit + * + * @see InfusedMagnet + * + */ public class InfusedHopper extends SimpleSlimefunItem { private final ItemSetting silent = new ItemSetting<>("silent", false); + private final ItemSetting toggleable = new ItemSetting<>("toggleable-with-redstone", false); private final ItemSetting radius = new DoubleRangeSetting("radius", 0.1, 3.5, Double.MAX_VALUE); + @ParametersAreNonnullByDefault public InfusedHopper(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); - addItemSetting(silent, radius); + addItemSetting(silent, radius, toggleable); } @Override @@ -45,17 +61,35 @@ public class InfusedHopper extends SimpleSlimefunItem { return; } - Location l = b.getLocation().add(0.5, 1.2, 0.5); - boolean sound = false; - double range = radius.getValue(); + // Check if this was enabled in the config + if (toggleable.getValue()) { + Hopper hopper = (Hopper) b.getBlockData(); + /** + * If the Hopper was disabled by a redstone signal, + * we just don't do anything. + */ + if (!hopper.isEnabled()) { + return; + } + } + + Location l = b.getLocation().add(0.5, 1.2, 0.5); + double range = radius.getValue(); + boolean playSound = false; + + // Check for any nearby Items that can be picked up for (Entity item : b.getWorld().getNearbyEntities(l, range, range, range, n -> isValidItem(l, n))) { item.setVelocity(new Vector(0, 0.1, 0)); item.teleport(l); - sound = true; + playSound = true; } - if (sound && !silent.getValue().booleanValue()) { + /** + * Play a sound if at least one item was teleported and + * the "silent" setting is set to false. + */ + if (playSound && !silent.getValue().booleanValue()) { b.getWorld().playSound(b.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.BLOCKS, 1F, 2F); } } @@ -67,7 +101,7 @@ public class InfusedHopper extends SimpleSlimefunItem { }; } - private boolean isValidItem(Location l, Entity entity) { + private boolean isValidItem(@Nonnull Location l, @Nonnull Entity entity) { if (entity instanceof Item && entity.isValid()) { Item item = (Item) entity; return !SlimefunUtils.hasNoPickupFlag(item) && item.getLocation().distanceSquared(l) > 0.25; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/MagicalZombiePills.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/MagicalZombiePills.java index 5a67191c2..4ce043bc6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/MagicalZombiePills.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/MagicalZombiePills.java @@ -1,6 +1,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.magical; import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.GameMode; import org.bukkit.Location; @@ -14,6 +15,7 @@ import org.bukkit.entity.ZombieVillager; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils; +import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; @@ -40,6 +42,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; */ public class MagicalZombiePills extends SimpleSlimefunItem implements NotPlaceable { + @ParametersAreNonnullByDefault public MagicalZombiePills(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack recipeOutput) { super(category, item, recipeType, recipe, recipeOutput); @@ -50,6 +53,12 @@ public class MagicalZombiePills extends SimpleSlimefunItem { Entity entity = e.getRightClicked(); + + if (e.isCancelled() || !SlimefunPlugin.getProtectionManager().hasPermission(e.getPlayer(), entity.getLocation(), ProtectableAction.INTERACT_ENTITY)) { + // They don't have permission to use it in this area + return; + } + Player p = e.getPlayer(); if (entity instanceof ZombieVillager) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundItem.java index 94f2d46bd..a9f854379 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundItem.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundItem.java @@ -4,6 +4,7 @@ import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.SoulboundRune; import io.github.thebusybiscuit.slimefun4.implementation.listeners.SoulboundListener; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/EnchantmentRune.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/EnchantmentRune.java similarity index 99% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/EnchantmentRune.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/EnchantmentRune.java index 7cf3a4362..4ca91cec6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/EnchantmentRune.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/EnchantmentRune.java @@ -1,4 +1,4 @@ -package io.github.thebusybiscuit.slimefun4.implementation.items.magical; +package io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes; import java.util.ArrayList; import java.util.Collection; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundRune.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/SoulboundRune.java similarity index 97% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundRune.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/SoulboundRune.java index 6415c6e23..dfde7821e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundRune.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/SoulboundRune.java @@ -1,4 +1,4 @@ -package io.github.thebusybiscuit.slimefun4.implementation.items.magical; +package io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes; import java.util.Collection; import java.util.Optional; @@ -14,6 +14,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemDropHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.SoulboundItem; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/VillagerRune.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/VillagerRune.java similarity index 84% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/VillagerRune.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/VillagerRune.java index 16eb4550a..4c9e5c451 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/VillagerRune.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/VillagerRune.java @@ -1,4 +1,4 @@ -package io.github.thebusybiscuit.slimefun4.implementation.items.magical; +package io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes; import java.util.concurrent.ThreadLocalRandom; @@ -10,7 +10,9 @@ import org.bukkit.entity.Villager.Profession; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils; +import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.slimefun4.core.handlers.EntityInteractHandler; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; @@ -33,6 +35,11 @@ public class VillagerRune extends SimpleSlimefunItem { @Override public EntityInteractHandler getItemHandler() { return (e, item, offhand) -> { + if (e.isCancelled() || !SlimefunPlugin.getProtectionManager().hasPermission(e.getPlayer(), e.getRightClicked().getLocation(), ProtectableAction.INTERACT_ENTITY)) { + // They don't have permission to use it in this area + return; + } + if (e.getRightClicked() instanceof Villager) { Villager v = (Villager) e.getRightClicked(); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/package-info.java new file mode 100644 index 000000000..6286c42b0 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/runes/package-info.java @@ -0,0 +1,5 @@ +/** + * This package holds any implementation of {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem} + * that is an ancient rune with functionality. + */ +package io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/StormStaff.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/StormStaff.java similarity index 99% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/StormStaff.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/StormStaff.java index 11a69daac..17a09f4b8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/StormStaff.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/StormStaff.java @@ -1,4 +1,4 @@ -package io.github.thebusybiscuit.slimefun4.implementation.items.magical; +package io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves; import java.util.List; @@ -72,7 +72,7 @@ public class StormStaff extends SimpleSlimefunItem { Location loc = p.getTargetBlock(null, 30).getLocation(); if (loc.getWorld() != null && loc.getChunk().isLoaded()) { - if (loc.getWorld().getPVP() && SlimefunPlugin.getProtectionManager().hasPermission(p, loc, ProtectableAction.PVP)) { + if (loc.getWorld().getPVP() && SlimefunPlugin.getProtectionManager().hasPermission(p, loc, ProtectableAction.ATTACK_PLAYER)) { e.cancel(); useItem(p, item, loc); } else { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/WaterStaff.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/WaterStaff.java similarity index 98% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/WaterStaff.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/WaterStaff.java index ca0f26c6d..5686a52be 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/WaterStaff.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/WaterStaff.java @@ -1,4 +1,4 @@ -package io.github.thebusybiscuit.slimefun4.implementation.items.magical; +package io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/WindStaff.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/WindStaff.java similarity index 76% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/WindStaff.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/WindStaff.java index 6ba3cddda..4be752939 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/WindStaff.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/WindStaff.java @@ -1,4 +1,4 @@ -package io.github.thebusybiscuit.slimefun4.implementation.items.magical; +package io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves; import org.bukkit.Bukkit; import org.bukkit.Effect; @@ -8,6 +8,8 @@ import org.bukkit.entity.Player; import org.bukkit.event.entity.FoodLevelChangeEvent; import org.bukkit.inventory.ItemStack; +import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting; +import io.github.thebusybiscuit.slimefun4.api.items.settings.IntRangeSetting; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; @@ -15,10 +17,20 @@ import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; +/** + * The {@link WindStaff} is a powerful staff which launches the {@link Player} forward when right clicked. + * + * @author TheBusyBiscuit + * + */ public class WindStaff extends SimpleSlimefunItem { + private final ItemSetting multiplier = new IntRangeSetting("power", 1, 4, Integer.MAX_VALUE); + public WindStaff(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); + + addItemSetting(multiplier); } @Override @@ -27,6 +39,7 @@ public class WindStaff extends SimpleSlimefunItem { Player p = e.getPlayer(); if (p.getFoodLevel() >= 2) { + // The isItem() check is here to prevent the MultiTool from consuming hunger if (isItem(e.getItem()) && p.getGameMode() != GameMode.CREATIVE) { FoodLevelChangeEvent event = new FoodLevelChangeEvent(p, p.getFoodLevel() - 2); Bukkit.getPluginManager().callEvent(event); @@ -36,7 +49,7 @@ public class WindStaff extends SimpleSlimefunItem { } } - p.setVelocity(p.getEyeLocation().getDirection().multiply(4)); + p.setVelocity(p.getEyeLocation().getDirection().multiply(multiplier.getValue())); p.getWorld().playSound(p.getLocation(), Sound.ENTITY_TNT_PRIMED, 1, 1); p.getWorld().playEffect(p.getLocation(), Effect.SMOKE, 1); p.setFallDistance(0F); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/package-info.java new file mode 100644 index 000000000..b937f26a3 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/staves/package-info.java @@ -0,0 +1,5 @@ +/** + * This package holds any implementation of {@link me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem} that is + * considered a "Magical Staff". + */ +package io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves; \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/MagicianTalisman.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/MagicianTalisman.java index 0909fb3f4..2d9ff2068 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/MagicianTalisman.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/MagicianTalisman.java @@ -57,6 +57,9 @@ public class MagicianTalisman extends Talisman { * * @param item * The {@link ItemStack} to find an {@link Enchantment} for + * @param existingEnchantments + * A {@link Set} containing the {@link Enchantment Enchantments} that currently exist on the + * {@link ItemStack} * * @return An applicable {@link TalismanEnchantment} or null */ diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/misc/StrangeNetherGoo.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/misc/StrangeNetherGoo.java index f5f79532f..a8d6d5a8c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/misc/StrangeNetherGoo.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/misc/StrangeNetherGoo.java @@ -21,7 +21,7 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.PiglinBarterDrop; import io.github.thebusybiscuit.slimefun4.core.handlers.EntityInteractHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; -import io.github.thebusybiscuit.slimefun4.implementation.items.magical.VillagerRune; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.VillagerRune; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/BackpackCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/AbstractCraftingTable.java similarity index 79% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/BackpackCrafter.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/AbstractCraftingTable.java index baf52187a..e5349c7e0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/BackpackCrafter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/AbstractCraftingTable.java @@ -4,6 +4,10 @@ import java.util.List; import java.util.Optional; import java.util.UUID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.BlockFace; @@ -20,6 +24,7 @@ import io.github.thebusybiscuit.slimefun4.core.multiblocks.MultiBlockMachine; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack; import io.github.thebusybiscuit.slimefun4.utils.PatternUtils; +import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; @@ -35,20 +40,24 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; * @see ArmorForge * */ -abstract class BackpackCrafter extends MultiBlockMachine { +abstract class AbstractCraftingTable extends MultiBlockMachine { - BackpackCrafter(Category category, SlimefunItemStack item, ItemStack[] recipe, BlockFace trigger) { + @ParametersAreNonnullByDefault + AbstractCraftingTable(Category category, SlimefunItemStack item, ItemStack[] recipe, BlockFace trigger) { super(category, item, recipe, trigger); } - protected Inventory createVirtualInventory(Inventory inv) { + @Nonnull + protected Inventory createVirtualInventory(@Nonnull Inventory inv) { Inventory fakeInv = Bukkit.createInventory(null, 9, "Fake Inventory"); for (int j = 0; j < inv.getContents().length; j++) { ItemStack stack = inv.getContents()[j]; - // Fixes #2103 - Properly simulating the consumption - // (which may leave behind empty buckets or glass bottles) + /** + * Fixes #2103 - Properly simulating the consumption + * (which may leave behind empty buckets or glass bottles) + */ if (stack != null) { stack = stack.clone(); ItemUtils.consumeItem(stack, true); @@ -60,18 +69,24 @@ abstract class BackpackCrafter extends MultiBlockMachine { return fakeInv; } + @ParametersAreNonnullByDefault protected void upgradeBackpack(Player p, Inventory inv, SlimefunBackpack backpack, ItemStack output) { - ItemStack backpackItem = null; + ItemStack input = null; for (int j = 0; j < 9; j++) { if (inv.getContents()[j] != null && inv.getContents()[j].getType() != Material.AIR && SlimefunItem.getByItem(inv.getContents()[j]) instanceof SlimefunBackpack) { - backpackItem = inv.getContents()[j]; + input = inv.getContents()[j]; break; } } + // Fixes #2574 - Carry over the Soulbound status + if (SlimefunUtils.isSoulbound(input)) { + SlimefunUtils.setSoulbound(output, true); + } + int size = backpack.getSize(); - Optional id = retrieveID(backpackItem, size); + Optional id = retrieveID(input, size); if (id.isPresent()) { for (int line = 0; line < output.getItemMeta().getLore().size(); line++) { @@ -100,7 +115,8 @@ abstract class BackpackCrafter extends MultiBlockMachine { } } - private Optional retrieveID(ItemStack backpack, int size) { + @Nonnull + private Optional retrieveID(@Nullable ItemStack backpack, int size) { if (backpack != null) { for (String line : backpack.getItemMeta().getLore()) { if (line.startsWith(ChatColors.color("&7ID: ")) && line.contains("#")) { 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 608a0e2f5..ff68a91e1 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 @@ -24,7 +24,7 @@ import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.api.Slimefun; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; -public class ArmorForge extends BackpackCrafter { +public class ArmorForge extends AbstractCraftingTable { public ArmorForge(Category category, SlimefunItemStack item) { super(category, item, new ItemStack[] { null, null, null, null, new ItemStack(Material.ANVIL), null, null, new CustomItem(Material.DISPENSER, "Dispenser (Facing up)"), null }, BlockFace.SELF); 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 b03a4f182..a55a804c2 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 @@ -23,7 +23,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.Slimefun; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; -public class EnhancedCraftingTable extends BackpackCrafter { +public class EnhancedCraftingTable extends AbstractCraftingTable { public EnhancedCraftingTable(Category category, SlimefunItemStack item) { super(category, item, new ItemStack[] { null, null, null, null, new ItemStack(Material.CRAFTING_TABLE), null, null, new ItemStack(Material.DISPENSER), null }, BlockFace.SELF); 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 c3ffd22df..fd0056e6a 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 @@ -24,7 +24,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.Slimefun; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; -public class MagicWorkbench extends BackpackCrafter { +public class MagicWorkbench extends AbstractCraftingTable { public MagicWorkbench(Category category, SlimefunItemStack item) { super(category, item, new ItemStack[] { null, null, null, null, null, null, new ItemStack(Material.BOOKSHELF), new ItemStack(Material.CRAFTING_TABLE), new ItemStack(Material.DISPENSER) }, BlockFace.UP); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java index 065c7277e..d0d7ac080 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/seasonal/ChristmasPresent.java @@ -2,14 +2,15 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.seasonal; import java.util.concurrent.ThreadLocalRandom; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.GameMode; import org.bukkit.block.Block; -import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; -import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; +import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.utils.FireworkUtils; import me.mrCookieSlime.Slimefun.Lists.RecipeType; @@ -26,10 +27,11 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; * @see EasterEgg * */ -public class ChristmasPresent extends SimpleSlimefunItem implements NotPlaceable { +public class ChristmasPresent extends SimpleSlimefunItem implements NotPlaceable { private final ItemStack[] gifts; + @ParametersAreNonnullByDefault public ChristmasPresent(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe, ItemStack... gifts) { super(category, item, recipeType, recipe); @@ -37,24 +39,21 @@ public class ChristmasPresent extends SimpleSlimefunItem impl } @Override - public BlockPlaceHandler getItemHandler() { - return new BlockPlaceHandler(false) { - - @Override - public void onPlayerPlace(BlockPlaceEvent e) { - e.setCancelled(true); + public ItemUseHandler getItemHandler() { + return e -> { + e.cancel(); + e.getClickedBlock().ifPresent(block -> { if (e.getPlayer().getGameMode() != GameMode.CREATIVE) { - ItemUtils.consumeItem(e.getItemInHand(), false); + ItemUtils.consumeItem(e.getItem(), false); } FireworkUtils.launchRandom(e.getPlayer(), 3); - Block b = e.getBlock(); + Block b = block.getRelative(e.getClickedFace()); ItemStack gift = gifts[ThreadLocalRandom.current().nextInt(gifts.length)].clone(); b.getWorld().dropItemNaturally(b.getLocation(), gift); - } - + }); }; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java index cf59ab075..16848bbd7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/GoldPan.java @@ -17,7 +17,6 @@ import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.cscorelib2.collections.RandomizedSet; import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; -import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting; import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem; import io.github.thebusybiscuit.slimefun4.core.handlers.EntityInteractHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; @@ -26,6 +25,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.ElectricGoldPan; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.AutomatedPanningMachine; +import io.github.thebusybiscuit.slimefun4.implementation.settings.GoldPanDrop; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; @@ -66,13 +66,14 @@ public class GoldPan extends SimpleSlimefunItem implements Recip return Material.GRAVEL; } + @Nonnull protected Set getGoldPanDrops() { Set settings = new HashSet<>(); - settings.add(new GoldPanDrop("chance.FLINT", 40, new ItemStack(Material.FLINT))); - settings.add(new GoldPanDrop("chance.CLAY", 20, new ItemStack(Material.CLAY_BALL))); - settings.add(new GoldPanDrop("chance.SIFTED_ORE", 35, SlimefunItems.SIFTED_ORE)); - settings.add(new GoldPanDrop("chance.IRON_NUGGET", 5, new ItemStack(Material.IRON_NUGGET))); + settings.add(new GoldPanDrop(this, "chance.FLINT", 40, new ItemStack(Material.FLINT))); + settings.add(new GoldPanDrop(this, "chance.CLAY", 20, new ItemStack(Material.CLAY_BALL))); + settings.add(new GoldPanDrop(this, "chance.SIFTED_ORE", 35, SlimefunItems.SIFTED_ORE)); + settings.add(new GoldPanDrop(this, "chance.IRON_NUGGET", 5, new ItemStack(Material.IRON_NUGGET))); return settings; } @@ -83,7 +84,13 @@ public class GoldPan extends SimpleSlimefunItem implements Recip updateRandomizer(); } - protected void updateRandomizer() { + /** + * Do not call this method directly. + * + * This method is for internal purposes only. + * It will update and re-calculate all weights in our {@link RandomizedSet}. + */ + public void updateRandomizer() { randomizer.clear(); for (GoldPanDrop setting : drops) { @@ -163,31 +170,4 @@ public class GoldPan extends SimpleSlimefunItem implements Recip return recipes; } - public class GoldPanDrop extends ItemSetting { - - private final ItemStack output; - - protected GoldPanDrop(String key, int defaultValue, ItemStack output) { - super(key, defaultValue); - - this.output = output; - } - - @Override - public boolean validateInput(Integer input) { - return super.validateInput(input) && input >= 0; - } - - public ItemStack getOutput() { - return output; - } - - @Override - public void update(Integer newValue) { - super.update(newValue); - updateRandomizer(); - } - - } - } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/NetherGoldPan.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/NetherGoldPan.java index 300b5a0cd..ac3838840 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/NetherGoldPan.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/NetherGoldPan.java @@ -3,34 +3,48 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.tools; import java.util.HashSet; import java.util.Set; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Material; import org.bukkit.inventory.ItemStack; +import io.github.thebusybiscuit.slimefun4.implementation.settings.GoldPanDrop; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; +/** + * The {@link NetherGoldPan} is a variant of the regular {@link GoldPan} + * which can be used on Soul Sand. + * + * @author TheBusyBiscuit + * + */ public class NetherGoldPan extends GoldPan { + @ParametersAreNonnullByDefault public NetherGoldPan(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); } @Override + @Nonnull protected Material getTargetMaterial() { return Material.SOUL_SAND; } @Override + @Nonnull protected Set getGoldPanDrops() { Set settings = new HashSet<>(); - settings.add(new GoldPanDrop("chance.QUARTZ", 50, new ItemStack(Material.QUARTZ))); - settings.add(new GoldPanDrop("chance.GOLD_NUGGET", 25, new ItemStack(Material.GOLD_NUGGET))); - settings.add(new GoldPanDrop("chance.NETHER_WART", 10, new ItemStack(Material.NETHER_WART))); - settings.add(new GoldPanDrop("chance.BLAZE_POWDER", 8, new ItemStack(Material.BLAZE_POWDER))); - settings.add(new GoldPanDrop("chance.GLOWSTONE_DUST", 5, new ItemStack(Material.GLOWSTONE_DUST))); - settings.add(new GoldPanDrop("chance.GHAST_TEAR", 2, new ItemStack(Material.GHAST_TEAR))); + settings.add(new GoldPanDrop(this, "chance.QUARTZ", 50, new ItemStack(Material.QUARTZ))); + settings.add(new GoldPanDrop(this, "chance.GOLD_NUGGET", 25, new ItemStack(Material.GOLD_NUGGET))); + settings.add(new GoldPanDrop(this, "chance.NETHER_WART", 10, new ItemStack(Material.NETHER_WART))); + settings.add(new GoldPanDrop(this, "chance.BLAZE_POWDER", 8, new ItemStack(Material.BLAZE_POWDER))); + settings.add(new GoldPanDrop(this, "chance.GLOWSTONE_DUST", 5, new ItemStack(Material.GLOWSTONE_DUST))); + settings.add(new GoldPanDrop(this, "chance.GHAST_TEAR", 2, new ItemStack(Material.GHAST_TEAR))); return settings; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java index d03f03729..7086c7897 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfContainment.java @@ -1,6 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.tools; -import java.util.List; +import javax.annotation.Nonnull; import org.bukkit.Material; import org.bukkit.block.Block; @@ -8,14 +8,13 @@ import org.bukkit.block.BlockState; import org.bukkit.block.CreatureSpawner; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; import io.github.thebusybiscuit.slimefun4.core.handlers.ToolUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; +import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.AbstractMonsterSpawner; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BrokenSpawner; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RepairedSpawner; -import io.github.thebusybiscuit.slimefun4.utils.ChatUtils; import io.papermc.lib.PaperLib; import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; @@ -54,33 +53,25 @@ public class PickaxeOfContainment extends SimpleSlimefunItem { }; } - private ItemStack breakSpawner(Block b) { - // If the spawner's BlockStorage has BlockInfo, then it's not a vanilla spawner and - // should not give a broken spawner. - ItemStack spawner = SlimefunItems.BROKEN_SPAWNER.clone(); + @Nonnull + private ItemStack breakSpawner(@Nonnull Block b) { + AbstractMonsterSpawner spawner; + /** + * If the spawner's BlockStorage has BlockInfo, then it's not a vanilla spawner + * and should not give a broken spawner but a repaired one instead. + */ if (BlockStorage.hasBlockInfo(b)) { - spawner = SlimefunItems.REPAIRED_SPAWNER.clone(); + spawner = (AbstractMonsterSpawner) SlimefunItems.REPAIRED_SPAWNER.getItem(); + } else { + spawner = (AbstractMonsterSpawner) SlimefunItems.BROKEN_SPAWNER.getItem(); } - ItemMeta im = spawner.getItemMeta(); - List lore = im.getLore(); - BlockState state = PaperLib.getBlockState(b, false).getState(); if (state instanceof CreatureSpawner) { EntityType entityType = ((CreatureSpawner) state).getSpawnedType(); - - for (int i = 0; i < lore.size(); i++) { - if (lore.get(i).contains("")) { - lore.set(i, lore.get(i).replace("", ChatUtils.humanize(entityType.name()))); - break; - } - } - - im.setLore(lore); - spawner.setItemMeta(im); - return spawner; + return spawner.getItemForEntityType(entityType); } return new ItemStack(Material.SPAWNER); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/ExplosiveBow.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/ExplosiveBow.java index e73b78ea3..1086221b2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/ExplosiveBow.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/ExplosiveBow.java @@ -2,9 +2,13 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.weapons; import java.util.Collection; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Bukkit; import org.bukkit.Particle; import org.bukkit.Sound; +import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.event.entity.EntityDamageByEntityEvent; @@ -33,6 +37,7 @@ public class ExplosiveBow extends SlimefunBow { private final ItemSetting range = new IntRangeSetting("explosion-range", 1, 3, Integer.MAX_VALUE); + @ParametersAreNonnullByDefault public ExplosiveBow(Category category, SlimefunItemStack item, ItemStack[] recipe) { super(category, item, recipe); @@ -44,8 +49,9 @@ public class ExplosiveBow extends SlimefunBow { return (e, target) -> { target.getWorld().spawnParticle(Particle.EXPLOSION_LARGE, target.getLocation(), 1); target.getWorld().playSound(target.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1, 1); + int radius = range.getValue(); - Collection entites = target.getWorld().getNearbyEntities(target.getLocation(), range.getValue(), range.getValue(), range.getValue(), entity -> entity instanceof LivingEntity && entity.isValid()); + Collection entites = target.getWorld().getNearbyEntities(target.getLocation(), radius, radius, radius, this::canDamage); for (Entity nearby : entites) { LivingEntity entity = (LivingEntity) nearby; @@ -71,6 +77,10 @@ public class ExplosiveBow extends SlimefunBow { }; } + private boolean canDamage(@Nonnull Entity n) { + return n instanceof LivingEntity && !(n instanceof ArmorStand) && n.isValid(); + } + private double calculateDamage(double distanceSquared, double originalDamage) { if (distanceSquared <= 0.05) { return originalDamage; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SeismicAxe.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SeismicAxe.java index 53d43e5c8..9f810486b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SeismicAxe.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SeismicAxe.java @@ -2,6 +2,9 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.weapons; import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Bukkit; import org.bukkit.Effect; import org.bukkit.Location; @@ -42,6 +45,7 @@ public class SeismicAxe extends SimpleSlimefunItem implements No private static final float DAMAGE = 6; private static final int RANGE = 10; + @ParametersAreNonnullByDefault public SeismicAxe(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); } @@ -96,7 +100,8 @@ public class SeismicAxe extends SimpleSlimefunItem implements No } } - private Block findGround(Block b) { + @Nonnull + private Block findGround(@Nonnull Block b) { if (b.getType() == Material.AIR) { for (int y = 0; y < b.getY(); y++) { Block block = b.getRelative(0, -y, 0); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java index 9f2d14170..aee48bdf8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/SwordOfBeheading.java @@ -3,8 +3,11 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.weapons; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Material; import org.bukkit.entity.Creeper; +import org.bukkit.entity.Monster; import org.bukkit.entity.Player; import org.bukkit.entity.Skeleton; import org.bukkit.entity.WitherSkeleton; @@ -21,6 +24,17 @@ import me.mrCookieSlime.Slimefun.Lists.RecipeType; import me.mrCookieSlime.Slimefun.Objects.Category; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; +/** + * The {@link SwordOfBeheading} is a special kind of sword which allows you to obtain + * {@link Zombie}, {@link Skeleton} and {@link Creeper} skulls when killing the respective {@link Monster}. + * Additionally, you can also obtain the head of a {@link Player} by killing them too. + * This sword also allows you to have a higher chance of getting the skull of a {@link WitherSkeleton} too. + * + * All chances are managed by an {@link ItemSetting} and can be configured. + * + * @author TheBusyBiscuit + * + */ public class SwordOfBeheading extends SimpleSlimefunItem { private final ItemSetting chanceZombie = new IntRangeSetting("chance.ZOMBIE", 0, 40, 100); @@ -29,6 +43,7 @@ public class SwordOfBeheading extends SimpleSlimefunItem { private final ItemSetting chanceCreeper = new IntRangeSetting("chance.CREEPER", 0, 40, 100); private final ItemSetting chancePlayer = new IntRangeSetting("chance.PLAYER", 0, 70, 100); + @ParametersAreNonnullByDefault public SwordOfBeheading(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); @@ -40,29 +55,40 @@ public class SwordOfBeheading extends SimpleSlimefunItem { return (e, entity, killer, item) -> { Random random = ThreadLocalRandom.current(); - if (e.getEntity() instanceof Zombie) { + switch (e.getEntityType()) { + case ZOMBIE: if (random.nextInt(100) < chanceZombie.getValue()) { e.getDrops().add(new ItemStack(Material.ZOMBIE_HEAD)); } - } else if (e.getEntity() instanceof WitherSkeleton) { - if (random.nextInt(100) < chanceWitherSkeleton.getValue()) { - e.getDrops().add(new ItemStack(Material.WITHER_SKELETON_SKULL)); - } - } else if (e.getEntity() instanceof Skeleton) { + break; + case SKELETON: if (random.nextInt(100) < chanceSkeleton.getValue()) { e.getDrops().add(new ItemStack(Material.SKELETON_SKULL)); } - } else if (e.getEntity() instanceof Creeper) { + break; + case CREEPER: if (random.nextInt(100) < chanceCreeper.getValue()) { e.getDrops().add(new ItemStack(Material.CREEPER_HEAD)); } - } else if (e.getEntity() instanceof Player && random.nextInt(100) < chancePlayer.getValue()) { - ItemStack skull = new ItemStack(Material.PLAYER_HEAD); - ItemMeta meta = skull.getItemMeta(); - ((SkullMeta) meta).setOwningPlayer((Player) e.getEntity()); - skull.setItemMeta(meta); + break; + case WITHER_SKELETON: + if (random.nextInt(100) < chanceWitherSkeleton.getValue()) { + e.getDrops().add(new ItemStack(Material.WITHER_SKELETON_SKULL)); + } + break; + case PLAYER: + if (random.nextInt(100) < chancePlayer.getValue()) { + ItemStack skull = new ItemStack(Material.PLAYER_HEAD); - e.getDrops().add(skull); + ItemMeta meta = skull.getItemMeta(); + ((SkullMeta) meta).setOwningPlayer((Player) e.getEntity()); + skull.setItemMeta(meta); + + e.getDrops().add(skull); + } + break; + default: + break; } }; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/VampireBlade.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/VampireBlade.java index f31bd37bd..16f1f2b9e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/VampireBlade.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/weapons/VampireBlade.java @@ -1,5 +1,8 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.weapons; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + import org.bukkit.Sound; import org.bukkit.attribute.Attribute; import org.bukkit.entity.LivingEntity; @@ -28,6 +31,7 @@ public class VampireBlade extends SlimefunItem { private static final double HEALING_AMOUNT = 4.0; private final ItemSetting chance = new IntRangeSetting("chance", 0, 45, 100); + @ParametersAreNonnullByDefault public VampireBlade(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) { super(category, item, recipeType, recipe); @@ -43,7 +47,7 @@ public class VampireBlade extends SlimefunItem { return chance.getValue(); } - public void heal(Player p) { + public void heal(@Nonnull Player p) { p.playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 0.7F, 0.7F); double health = p.getHealth() + HEALING_AMOUNT; double maxHealth = p.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue(); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/AncientAltarListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/AncientAltarListener.java index 559a7a94b..646ed0476 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/AncientAltarListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/AncientAltarListener.java @@ -1,7 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.listeners; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -17,6 +16,7 @@ import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.entity.EntityType; import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.event.Event.Result; @@ -25,7 +25,6 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils; import io.github.thebusybiscuit.cscorelib2.item.CustomItem; @@ -36,6 +35,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AltarRecipe; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientAltar; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal; +import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RepairedSpawner; import io.github.thebusybiscuit.slimefun4.implementation.tasks.AncientAltarTask; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; @@ -128,7 +128,7 @@ public class AncientAltarListener implements Listener { return; } - if (!SlimefunPlugin.getProtectionManager().hasPermission(p, pedestal, ProtectableAction.ACCESS_INVENTORIES)) { + if (!SlimefunPlugin.getProtectionManager().hasPermission(p, pedestal, ProtectableAction.INTERACT_BLOCK)) { SlimefunPlugin.getLocalization().sendMessage(p, "inventory.no-access", true); return; } @@ -162,7 +162,7 @@ public class AncientAltarListener implements Listener { } private void useAltar(@Nonnull Block altar, @Nonnull Player p) { - if (!SlimefunPlugin.getProtectionManager().hasPermission(p, altar, ProtectableAction.ACCESS_INVENTORIES)) { + if (!SlimefunPlugin.getProtectionManager().hasPermission(p, altar, ProtectableAction.INTERACT_BLOCK)) { SlimefunPlugin.getLocalization().sendMessage(p, "inventory.no-access", true); return; } @@ -312,11 +312,8 @@ public class AncientAltarListener implements Listener { return Optional.empty(); } - ItemStack spawner = SlimefunItems.REPAIRED_SPAWNER.clone(); - ItemMeta im = spawner.getItemMeta(); - im.setLore(Arrays.asList(wrapper.getItemMeta().getLore().get(0))); - spawner.setItemMeta(im); - return Optional.of(spawner); + RepairedSpawner spawner = (RepairedSpawner) SlimefunItems.REPAIRED_SPAWNER.getItem(); + return Optional.of(spawner.getItemForEntityType(spawner.getEntityType(wrapper).orElse(EntityType.PIG))); } return checkRecipe(wrapper, items); 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 815cb6c6e..080372fc1 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 @@ -22,6 +22,7 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.inventory.ItemStack; +import io.github.thebusybiscuit.cscorelib2.protection.ProtectableAction; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockBreakHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockPlaceHandler; @@ -152,6 +153,9 @@ public class BlockListener implements Listener { if (!drops.isEmpty()) { e.getBlock().setType(Material.AIR); + // Notify plugins like CoreProtect + SlimefunPlugin.getProtectionManager().logAction(e.getPlayer(), e.getBlock(), ProtectableAction.BREAK_BLOCK); + if (e.isDropItems()) { for (ItemStack drop : drops) { if (drop != null && drop.getType() != Material.AIR) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockPhysicsListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockPhysicsListener.java index eb4aeaba4..fd4bbd44e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockPhysicsListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BlockPhysicsListener.java @@ -53,7 +53,7 @@ public class BlockPhysicsListener implements Listener { } } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onPistonExtend(BlockPistonExtendEvent e) { if (BlockStorage.hasBlockInfo(e.getBlock())) { e.setCancelled(true); @@ -67,7 +67,7 @@ public class BlockPhysicsListener implements Listener { } } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onPistonRetract(BlockPistonRetractEvent e) { if (BlockStorage.hasBlockInfo(e.getBlock())) { e.setCancelled(true); @@ -85,8 +85,19 @@ public class BlockPhysicsListener implements Listener { public void onLiquidFlow(BlockFromToEvent e) { Block block = e.getToBlock(); - if (SlimefunTag.FLUID_SENSITIVE_MATERIALS.isTagged(block.getType()) && BlockStorage.hasBlockInfo(block)) { - e.setCancelled(true); + // Check if this Material can be destroyed by fluids + if (SlimefunTag.FLUID_SENSITIVE_MATERIALS.isTagged(block.getType())) { + // Check if this Block holds any data + if (BlockStorage.hasBlockInfo(block)) { + e.setCancelled(true); + } else { + Location loc = block.getLocation(); + + // Fixes #2496 - Make sure it is not a moving block + if (SlimefunPlugin.getTickerTask().isReserved(loc)) { + e.setCancelled(true); + } + } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/CoolerListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/CoolerListener.java index e7c779b24..87a5a88a6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/CoolerListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/CoolerListener.java @@ -50,29 +50,28 @@ public class CoolerListener implements Listener { @EventHandler public void onHungerLoss(FoodLevelChangeEvent e) { - if (cooler.isDisabled() || !(e.getEntity() instanceof Player)) { - return; - } + if (e.getEntity() instanceof Player) { + Player p = (Player) e.getEntity(); - Player p = (Player) e.getEntity(); - - if (e.getFoodLevel() < p.getFoodLevel()) { - checkAndConsume(p); + if (e.getFoodLevel() < p.getFoodLevel()) { + checkAndConsume(p); + } } } @EventHandler public void onHungerDamage(EntityDamageEvent e) { - if (cooler.isDisabled() || !(e.getEntity() instanceof Player)) { - return; - } - - if (e.getCause() == DamageCause.STARVATION) { + if (e.getEntity() instanceof Player && e.getCause() == DamageCause.STARVATION) { checkAndConsume((Player) e.getEntity()); } } private void checkAndConsume(@Nonnull Player p) { + if (cooler == null || cooler.isDisabled()) { + // Do not proceed if the Cooler was disabled + return; + } + for (ItemStack item : p.getInventory().getContents()) { if (cooler.isItem(item)) { if (Slimefun.hasUnlocked(p, cooler, true)) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraCrashListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraImpactListener.java similarity index 95% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraCrashListener.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraImpactListener.java index ad5f25c6c..8b065acc6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraCrashListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraImpactListener.java @@ -27,9 +27,9 @@ import me.mrCookieSlime.Slimefun.api.Slimefun; * * @see ElytraCap */ -public class ElytraCrashListener implements Listener { +public class ElytraImpactListener implements Listener { - public ElytraCrashListener(@Nonnull SlimefunPlugin plugin) { + public ElytraImpactListener(@Nonnull SlimefunPlugin plugin) { plugin.getServer().getPluginManager().registerEvents(this, plugin); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/EntityInteractionListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/EntityInteractionListener.java index 2fd7ad0e9..3f22f6c60 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/EntityInteractionListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/EntityInteractionListener.java @@ -10,15 +10,17 @@ import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; +import io.github.thebusybiscuit.slimefun4.api.items.ItemState; import io.github.thebusybiscuit.slimefun4.core.handlers.EntityInteractHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.Slimefun; /** - * The Listener class responsible for a {@link Player} interacting with an {@link Entity}. + * The {@link Listener} responsible for a {@link Player} interacting with an {@link Entity}. * * @author Linox + * @author TheBusyBiscuit * * @see EntityInteractHandler * @@ -45,8 +47,17 @@ public class EntityInteractionListener implements Listener { SlimefunItem sfItem = SlimefunItem.getByItem(itemStack); - if (sfItem != null && Slimefun.hasUnlocked(e.getPlayer(), sfItem, true)) { - sfItem.callItemHandler(EntityInteractHandler.class, handler -> handler.onInteract(e, itemStack, e.getHand() == EquipmentSlot.OFF_HAND)); + if (sfItem != null) { + if (Slimefun.hasUnlocked(e.getPlayer(), sfItem, true)) { + sfItem.callItemHandler(EntityInteractHandler.class, handler -> handler.onInteract(e, itemStack, e.getHand() == EquipmentSlot.OFF_HAND)); + } else if (sfItem.getState() != ItemState.VANILLA_FALLBACK) { + /** + * If an Item is disabled, we don't want it to fallback to the vanilla behaviour + * unless it is a Vanilla Item of course. + * Related to Issue #2446 + */ + e.setCancelled(true); + } } } } \ No newline at end of file diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/FireworksListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/FireworksListener.java index 99e523b71..dab8a171a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/FireworksListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/FireworksListener.java @@ -4,13 +4,22 @@ import javax.annotation.Nonnull; import org.bukkit.ChatColor; import org.bukkit.entity.Firework; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.inventory.meta.FireworkMeta; +import io.github.thebusybiscuit.slimefun4.core.researching.Research; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +/** + * This {@link Listener} makes sure that any {@link Firework} caused by a {@link Player} + * unlocking a {@link Research} does not cause damage to be dealt. + * + * @author TheBusyBiscuit + * + */ public class FireworksListener implements Listener { public FireworksListener(@Nonnull SlimefunPlugin plugin) { @@ -23,6 +32,12 @@ public class FireworksListener implements Listener { Firework firework = (Firework) e.getDamager(); FireworkMeta meta = firework.getFireworkMeta(); + /** + * We could use Peristent Data for this in the future, but ItemMeta display names + * work pretty reliably too and they don't cause any memory leaks like metadata. + * + * Entity display names do not work either as Firework cannot be named. + */ if (meta.hasDisplayName() && meta.getDisplayName().equals(ChatColor.GREEN + "Slimefun Research")) { e.setCancelled(true); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/GadgetsListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/GadgetsListener.java index 833cac81c..2e2b0fecf 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/GadgetsListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/GadgetsListener.java @@ -16,7 +16,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets. import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedMagnet; import io.github.thebusybiscuit.slimefun4.implementation.tasks.JetBootsTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.JetpackTask; -import io.github.thebusybiscuit.slimefun4.implementation.tasks.MagnetTask; +import io.github.thebusybiscuit.slimefun4.implementation.tasks.InfusedMagnetTask; import io.github.thebusybiscuit.slimefun4.implementation.tasks.ParachuteTask; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; @@ -32,7 +32,7 @@ import me.mrCookieSlime.Slimefun.api.Slimefun; * @see JetpackTask * @see JetBootsTask * @see ParachuteTask - * @see MagnetTask + * @see InfusedMagnetTask * */ public class GadgetsListener implements Listener { @@ -60,7 +60,7 @@ public class GadgetsListener implements Listener { InfusedMagnet magnet = (InfusedMagnet) SlimefunItems.INFUSED_MAGNET.getItem(); if (Slimefun.hasUnlocked(p, magnet, true)) { - new MagnetTask(p, magnet.getRadius()).scheduleRepeating(0, 8); + new InfusedMagnetTask(p, magnet.getRadius()).scheduleRepeating(0, 8); } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/IronGolemListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/IronGolemListener.java index 8bb6e4c2e..36030a722 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/IronGolemListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/IronGolemListener.java @@ -19,7 +19,6 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; /** * This {@link Listener} makes sure that an {@link IronGolem} cannot be healed with * a {@link SlimefunItem}. - * This fixes Issue 1332. * * @author TheBusyBiscuit * @@ -42,6 +41,7 @@ public class IronGolemListener implements Listener { item = inv.getItemInOffHand(); } + // Check if the Golem was clicked using an Iron Ingot if (item != null && item.getType() == Material.IRON_INGOT) { SlimefunItem sfItem = SlimefunItem.getByItem(item); @@ -49,8 +49,10 @@ public class IronGolemListener implements Listener { e.setCancelled(true); SlimefunPlugin.getLocalization().sendMessage(e.getPlayer(), "messages.no-iron-golem-heal"); - // This is just there to update the Inventory... - // Somehow cancelling it isn't enough. + /** + * This is just there to update the Inventory... + * Somehow cancelling it isn't enough. + */ if (e.getHand() == EquipmentSlot.HAND) { inv.setItemInMainHand(item); } else if (e.getHand() == EquipmentSlot.OFF_HAND) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ItemDropListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ItemDropListener.java new file mode 100644 index 000000000..9141d6e68 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ItemDropListener.java @@ -0,0 +1,35 @@ +package io.github.thebusybiscuit.slimefun4.implementation.listeners; + +import javax.annotation.Nonnull; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerDropItemEvent; + +import io.github.thebusybiscuit.slimefun4.core.handlers.ItemDropHandler; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; +import me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler; + +/** + * Listens to the {@link PlayerDropItemEvent} to call any {@link ItemDropHandler}. + * + * @author TheBusyBiscuit + * + * @see ItemDropHandler + */ +public class ItemDropListener implements Listener { + + public ItemDropListener(@Nonnull SlimefunPlugin plugin) { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @EventHandler + public void onItemDrop(PlayerDropItemEvent e) { + for (ItemHandler handler : SlimefunItem.getPublicItemHandlers(ItemDropHandler.class)) { + if (((ItemDropHandler) handler).onItemDrop(e, e.getPlayer(), e.getItemDrop())) { + return; + } + } + } +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemInteractListener.java similarity index 66% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemListener.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemInteractListener.java index 59ca7b6a6..ee276c3d3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunItemInteractListener.java @@ -7,56 +7,78 @@ import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.Event.Result; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent; import io.github.thebusybiscuit.slimefun4.core.handlers.BlockUseHandler; -import io.github.thebusybiscuit.slimefun4.core.handlers.ItemDropHandler; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; -import me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler; import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.Slimefun; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu; import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset; import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu; -public class SlimefunItemListener implements Listener { +/** + * This {@link Listener} listens to the {@link PlayerInteractEvent}. + * It is also responsible for calling our {@link PlayerRightClickEvent} and triggering any + * {@link ItemUseHandler} or {@link BlockUseHandler} for the clicked {@link ItemStack} or {@link Block}. + * + * @author TheBusyBiscuit + * @author Liruxo + * + * @see PlayerRightClickEvent + * @see ItemUseHandler + * @see BlockUseHandler + * + */ +public class SlimefunItemInteractListener implements Listener { - public SlimefunItemListener(@Nonnull SlimefunPlugin plugin) { + public SlimefunItemInteractListener(@Nonnull SlimefunPlugin plugin) { plugin.getServer().getPluginManager().registerEvents(this, plugin); } @EventHandler public void onRightClick(PlayerInteractEvent e) { if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) { + // Exclude the Debug Fish here because it is handled in a seperate Listener if (SlimefunUtils.isItemSimilar(e.getItem(), SlimefunItems.DEBUG_FISH, true)) { return; } + // Fire our custom Event PlayerRightClickEvent event = new PlayerRightClickEvent(e); Bukkit.getPluginManager().callEvent(event); boolean itemUsed = e.getHand() == EquipmentSlot.OFF_HAND; + // Only handle the Item if it hasn't been denied if (event.useItem() != Result.DENY) { rightClickItem(e, event, itemUsed); } - if (!itemUsed && event.useBlock() != Result.DENY && !rightClickBlock(e, event)) { + if (!itemUsed && event.useBlock() != Result.DENY && !rightClickBlock(event)) { return; } + /** + * If the original Event was not denied but the custom one was, + * we also want to deny the original one. + * This only applies for non-denied events because we do not want to + * override any protective checks. + */ + if (e.useInteractedBlock() != Result.DENY) { e.setUseInteractedBlock(event.useBlock()); } @@ -67,7 +89,6 @@ public class SlimefunItemListener implements Listener { } } - @Nonnull @ParametersAreNonnullByDefault private boolean rightClickItem(PlayerInteractEvent e, PlayerRightClickEvent event, boolean defaultValue) { Optional optional = event.getSlimefunItem(); @@ -84,12 +105,12 @@ public class SlimefunItemListener implements Listener { } @ParametersAreNonnullByDefault - private boolean rightClickBlock(PlayerInteractEvent e, PlayerRightClickEvent event) { + private boolean rightClickBlock(PlayerRightClickEvent event) { Optional optional = event.getSlimefunBlock(); if (optional.isPresent()) { - if (!Slimefun.hasUnlocked(e.getPlayer(), optional.get(), true)) { - e.setCancelled(true); + if (!Slimefun.hasUnlocked(event.getPlayer(), optional.get(), true)) { + event.getInteractEvent().setCancelled(true); return false; } @@ -97,10 +118,10 @@ public class SlimefunItemListener implements Listener { if (!interactable) { String id = optional.get().getId(); - Player p = e.getPlayer(); + Player p = event.getPlayer(); if (BlockMenuPreset.isInventory(id)) { - openInventory(p, id, e, event); + openInventory(p, id, event.getInteractEvent().getClickedBlock(), event); return false; } } @@ -110,22 +131,22 @@ public class SlimefunItemListener implements Listener { } @ParametersAreNonnullByDefault - private void openInventory(Player p, String id, PlayerInteractEvent e, PlayerRightClickEvent event) { - if (!p.isSneaking() || Material.AIR == event.getItem().getType()) { - e.setCancelled(true); + private void openInventory(Player p, String id, Block clickedBlock, PlayerRightClickEvent event) { + if (!p.isSneaking() || event.getItem().getType() == Material.AIR) { + event.getInteractEvent().setCancelled(true); if (BlockStorage.hasUniversalInventory(id)) { UniversalBlockMenu menu = BlockStorage.getUniversalInventory(id); - if (menu.canOpen(e.getClickedBlock(), p)) { + if (menu.canOpen(clickedBlock, p)) { menu.open(p); } else { SlimefunPlugin.getLocalization().sendMessage(p, "inventory.no-access", true); } - } else if (BlockStorage.getStorage(e.getClickedBlock().getWorld()).hasInventory(e.getClickedBlock().getLocation())) { - BlockMenu menu = BlockStorage.getInventory(e.getClickedBlock().getLocation()); + } else if (BlockStorage.getStorage(clickedBlock.getWorld()).hasInventory(clickedBlock.getLocation())) { + BlockMenu menu = BlockStorage.getInventory(clickedBlock.getLocation()); - if (menu.canOpen(e.getClickedBlock(), p)) { + if (menu.canOpen(clickedBlock, p)) { menu.open(p); } else { SlimefunPlugin.getLocalization().sendMessage(p, "inventory.no-access", true); @@ -134,13 +155,4 @@ public class SlimefunItemListener implements Listener { } } - @EventHandler - public void onItemDrop(PlayerDropItemEvent e) { - for (ItemHandler handler : SlimefunItem.getPublicItemHandlers(ItemDropHandler.class)) { - if (((ItemDropHandler) handler).onItemDrop(e, e.getPlayer(), e.getItemDrop())) { - return; - } - } - } - } 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 44da98b2e..240f5ebcc 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 @@ -6,7 +6,6 @@ import java.util.Map; import java.util.UUID; import javax.annotation.Nonnull; -import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -15,9 +14,18 @@ import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.inventory.ItemStack; +import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; +/** + * This {@link Listener} is responsible for handling any {@link Soulbound} items. + * A {@link Soulbound} {@link ItemStack} will not drop upon a {@link Player Player's} death. + * Instead the {@link ItemStack} is saved and given back to the {@link Player} when they respawn. + * + * @author TheBusyBiscuit + * + */ public class SoulboundListener implements Listener { private final Map> soulbound = new HashMap<>(); @@ -28,6 +36,7 @@ public class SoulboundListener implements Listener { @EventHandler public void onDamage(PlayerDeathEvent e) { + Map items = new HashMap<>(); Player p = e.getEntity(); for (int slot = 0; slot < p.getInventory().getSize(); slot++) { @@ -35,10 +44,19 @@ public class SoulboundListener implements Listener { // Store soulbound items for later retrieval if (SlimefunUtils.isSoulbound(item)) { - storeItem(p.getUniqueId(), slot, item); + items.put(slot, item); } } + // There shouldn't even be any items in there, but let's be extra safe! + Map existingItems = soulbound.get(p.getUniqueId()); + + if (existingItems == null) { + soulbound.put(p.getUniqueId(), items); + } else { + existingItems.putAll(items); + } + // Remove soulbound items from our drops Iterator drops = e.getDrops().iterator(); while (drops.hasNext()) { @@ -52,16 +70,10 @@ public class SoulboundListener implements Listener { @EventHandler public void onRespawn(PlayerRespawnEvent e) { - retrieveItems(e.getPlayer()); + returnSoulboundItems(e.getPlayer()); } - @ParametersAreNonnullByDefault - private void storeItem(UUID uuid, int slot, ItemStack item) { - Map items = soulbound.computeIfAbsent(uuid, uid -> new HashMap<>()); - items.put(slot, item); - } - - private void retrieveItems(@Nonnull Player p) { + private void returnSoulboundItems(@Nonnull Player p) { Map items = soulbound.remove(p.getUniqueId()); if (items != null) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TeleporterListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TeleporterListener.java index 3d646ae49..158d38092 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TeleporterListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TeleporterListener.java @@ -35,6 +35,7 @@ public class TeleporterListener implements Listener { } String id = BlockStorage.checkID(e.getClickedBlock()); + if (id == null) { return; } @@ -55,7 +56,13 @@ public class TeleporterListener implements Listener { @ParametersAreNonnullByDefault private boolean isTeleporterPad(String id, Block b, UUID uuid) { - return id.equals(SlimefunItems.GPS_ACTIVATION_DEVICE_SHARED.getItemId()) || (id.equals(SlimefunItems.GPS_ACTIVATION_DEVICE_PERSONAL.getItemId()) && BlockStorage.getLocationInfo(b.getLocation(), "owner").equals(uuid.toString())); + if (id.equals(SlimefunItems.GPS_ACTIVATION_DEVICE_SHARED.getItemId())) { + return true; + } else if (id.equals(SlimefunItems.GPS_ACTIVATION_DEVICE_PERSONAL.getItemId())) { + return BlockStorage.getLocationInfo(b.getLocation(), "owner").equals(uuid.toString()); + } else { + return false; + } } private boolean checkForPylons(@Nonnull Block teleporter) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/VampireBladeListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/VampireBladeListener.java index d8abaadc7..31e7444ac 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/VampireBladeListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/VampireBladeListener.java @@ -34,7 +34,7 @@ public class VampireBladeListener implements Listener { this.blade = blade; } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onDamage(EntityDamageByEntityEvent e) { if (blade.isDisabled()) { return; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/WorldListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/WorldListener.java index a18400451..315bc295d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/WorldListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/WorldListener.java @@ -22,7 +22,7 @@ public class WorldListener implements Listener { @EventHandler public void onWorldLoad(WorldLoadEvent e) { SlimefunPlugin.getWorldSettingsService().load(e.getWorld()); - BlockStorage.getForcedStorage(e.getWorld()); + BlockStorage.getOrCreate(e.getWorld()); } @EventHandler diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/BrewingStandListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/BrewingStandListener.java index 9f3d40f2f..f43907fe7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/BrewingStandListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/BrewingStandListener.java @@ -9,6 +9,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; @@ -21,6 +22,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; * * @author VoidAngel * @author SoSeDiK + * @author CURVX * */ public class BrewingStandListener implements SlimefunCraftingListener { @@ -52,4 +54,10 @@ public class BrewingStandListener implements SlimefunCraftingListener { } } + @EventHandler + public void hopperOnBrew(InventoryMoveItemEvent e) { + if (e.getDestination().getType() == InventoryType.BREWING && isUnallowed(e.getItem())) { + e.setCancelled(true); + } + } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/SlimefunCraftingListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/SlimefunCraftingListener.java index 6fdba5ab0..05439dbf0 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/SlimefunCraftingListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/crafting/SlimefunCraftingListener.java @@ -17,13 +17,8 @@ interface SlimefunCraftingListener extends Listener { } else { SlimefunItem sfItem1 = SlimefunItem.getByItem(item1); SlimefunItem sfItem2 = SlimefunItem.getByItem(item2); - - if (isUnallowed(sfItem1) || isUnallowed(sfItem2)) { - return true; - } + return isUnallowed(sfItem1) || isUnallowed(sfItem2); } - - return false; } default boolean isUnallowed(@Nullable ItemStack item) { @@ -32,7 +27,7 @@ interface SlimefunCraftingListener extends Listener { } SlimefunItem sfItem = SlimefunItem.getByItem(item); - return !(sfItem instanceof VanillaItem) && !sfItem.isDisabled(); + return isUnallowed(sfItem); } default boolean isUnallowed(@Nullable SlimefunItem item) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/settings/ClimbableSurface.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/settings/ClimbableSurface.java index d88191053..fa2e7319a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/settings/ClimbableSurface.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/settings/ClimbableSurface.java @@ -10,7 +10,8 @@ import io.github.thebusybiscuit.slimefun4.api.items.settings.DoubleRangeSetting; import io.github.thebusybiscuit.slimefun4.implementation.items.tools.ClimbingPick; /** - * This is an {@link ItemSetting} + * This is an {@link ItemSetting} that manages the efficiency of climbing + * a certain {@link Material} with the {@link ClimbingPick}. * * @author TheBusyBiscuit * @@ -32,6 +33,7 @@ public class ClimbableSurface extends DoubleRangeSetting { */ public ClimbableSurface(@Nonnull Material surface, double defaultValue) { super("launch-amounts." + surface.name(), 0, defaultValue, Double.MAX_VALUE); + this.type = surface; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/settings/GoldPanDrop.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/settings/GoldPanDrop.java new file mode 100644 index 000000000..8202589a2 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/settings/GoldPanDrop.java @@ -0,0 +1,40 @@ +package io.github.thebusybiscuit.slimefun4.implementation.settings; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + +import org.bukkit.inventory.ItemStack; + +import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting; +import io.github.thebusybiscuit.slimefun4.implementation.items.tools.GoldPan; + +public class GoldPanDrop extends ItemSetting { + + private final GoldPan goldPan; + private final ItemStack output; + + @ParametersAreNonnullByDefault + public GoldPanDrop(GoldPan goldPan, String key, int defaultValue, ItemStack output) { + super(key, defaultValue); + + this.goldPan = goldPan; + this.output = output; + } + + @Override + public boolean validateInput(Integer input) { + return super.validateInput(input) && input >= 0; + } + + @Nonnull + public ItemStack getOutput() { + return output; + } + + @Override + public void update(Integer newValue) { + super.update(newValue); + goldPan.updateRandomizer(); + } + +} \ No newline at end of file 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 e39b73470..996142f26 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 @@ -28,14 +28,10 @@ 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.EnhancedCraftingTable; 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; import io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.Smeltery; -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.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.AContainer; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.abstractItems.MachineRecipe; @@ -75,7 +71,11 @@ public final class PostSetup { Slimefun.getLogger().log(Level.WARNING, "Removed bugged Item ('NULL?')"); iterator.remove(); } else { - item.load(); + try { + item.load(); + } catch (Exception | LinkageError x) { + item.error("Failed to properly load this Item", x); + } } } @@ -115,33 +115,25 @@ public final class PostSetup { SlimefunPlugin.getRegistry().setAutoLoadingMode(true); } + /** + * This method counts the amount of {@link SlimefunItem SlimefunItems} registered + * by Slimefun itself and not by any addons. + * + * @return The amount of {@link SlimefunItem SlimefunItems} added by Slimefun itself + */ private static int countNonAddonItems() { - return (int) SlimefunPlugin.getRegistry().getEnabledSlimefunItems().stream().filter(item -> item.getAddon() instanceof SlimefunPlugin).count(); + // @formatter:off + return (int) SlimefunPlugin.getRegistry().getEnabledSlimefunItems().stream() + .filter(item -> item.getAddon() instanceof SlimefunPlugin) + .count(); + // @formatter:on } private static void loadAutomaticCraftingChamber() { AutomatedCraftingChamber crafter = (AutomatedCraftingChamber) SlimefunItems.AUTOMATED_CRAFTING_CHAMBER.getItem(); if (crafter != null) { - 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++; - } - - SlimefunPlugin.getRegistry().getAutomatedCraftingChamberRecipes().put(builder.toString(), RecipeType.getRecipeOutputList(machine, inputs)); - } - + crafter.loadRecipes(); } } 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 5db947f06..597e1d8dd 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 @@ -51,7 +51,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.Crucible; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.EnhancedFurnace; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.HardenedGlass; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.HologramProjector; -import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.InfusedHopper; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RainbowBlock; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.RepairedSpawner; import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.UnplaceableBlock; @@ -129,20 +128,21 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.gps.PersonalActiv import io.github.thebusybiscuit.slimefun4.implementation.items.gps.Teleporter; import io.github.thebusybiscuit.slimefun4.implementation.items.gps.TeleporterPylon; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.BeeWings; -import io.github.thebusybiscuit.slimefun4.implementation.items.magical.EnchantmentRune; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfernalBonemeal; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedHopper; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedMagnet; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.KnowledgeFlask; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.KnowledgeTome; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.MagicEyeOfEnder; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.MagicalZombiePills; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.SoulboundItem; -import io.github.thebusybiscuit.slimefun4.implementation.items.magical.SoulboundRune; -import io.github.thebusybiscuit.slimefun4.implementation.items.magical.StormStaff; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.TelepositionScroll; -import io.github.thebusybiscuit.slimefun4.implementation.items.magical.VillagerRune; -import io.github.thebusybiscuit.slimefun4.implementation.items.magical.WaterStaff; -import io.github.thebusybiscuit.slimefun4.implementation.items.magical.WindStaff; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.EnchantmentRune; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.SoulboundRune; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.runes.VillagerRune; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.StormStaff; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.WaterStaff; +import io.github.thebusybiscuit.slimefun4.implementation.items.magical.staves.WindStaff; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.talismans.MagicianTalisman; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.talismans.Talisman; import io.github.thebusybiscuit.slimefun4.implementation.items.medical.Bandage; @@ -202,7 +202,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; /** - * This static utility class holds the recipes of all items. + * This class holds the recipes of all items. * This is the place where all items from Slimefun are registered. * */ diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/PlayerTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/AbstractPlayerTask.java similarity index 59% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/PlayerTask.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/AbstractPlayerTask.java index ee1097858..05bd2acb7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/PlayerTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/AbstractPlayerTask.java @@ -7,16 +7,16 @@ import org.bukkit.entity.Player; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; -abstract class PlayerTask implements Runnable { +abstract class AbstractPlayerTask implements Runnable { - protected int id; - protected Player p; + protected final Player p; + private int id; - PlayerTask(@Nonnull Player p) { + AbstractPlayerTask(@Nonnull Player p) { this.p = p; } - public void setID(int id) { + private void setID(int id) { this.id = id; } @@ -29,21 +29,28 @@ abstract class PlayerTask implements Runnable { } @Override - public void run() { + public final void run() { if (isValid()) { executeTask(); } } /** - * This method checks if this {@link PlayerTask} should be continued or cancelled. - * It will also cancel this {@link PlayerTask} if it became invalid. + * This method cancels this {@link AbstractPlayerTask}. + */ + public final void cancel() { + Bukkit.getScheduler().cancelTask(id); + } + + /** + * This method checks if this {@link AbstractPlayerTask} should be continued or cancelled. + * It will also cancel this {@link AbstractPlayerTask} if it became invalid. * - * @return Whether this {@link PlayerTask} is still valid + * @return Whether this {@link AbstractPlayerTask} is still valid */ protected boolean isValid() { if (!p.isOnline() || !p.isValid() || p.isDead() || !p.isSneaking()) { - Bukkit.getScheduler().cancelTask(id); + cancel(); return false; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ArmorTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ArmorTask.java index 8b92ac872..ed1c04fa3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ArmorTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ArmorTask.java @@ -26,8 +26,10 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.armor.SlimefunArmorPiece; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.SolarHelmet; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; +import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.Slimefun; +import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; /** * The {@link ArmorTask} is responsible for handling {@link PotionEffect PotionEffects} for @@ -167,8 +169,16 @@ public class ArmorTask implements Runnable { return false; } - for (SlimefunItem radioactiveItem : SlimefunPlugin.getRegistry().getRadioactiveItems()) { - if (radioactiveItem.isItem(item) && Slimefun.isEnabled(p, radioactiveItem, true)) { + Set radioactiveItems = SlimefunPlugin.getRegistry().getRadioactiveItems(); + ItemStack subject = item; + + if (!(item instanceof SlimefunItemStack) && radioactiveItems.size() > 1) { + // Performance optimization to reduce ItemMeta calls + subject = new ItemStackWrapper(item); + } + + for (SlimefunItem radioactiveItem : radioactiveItems) { + if (radioactiveItem.isItem(subject) && Slimefun.isEnabled(p, radioactiveItem, true)) { // If the item is enabled in the world, then make radioactivity do its job SlimefunPlugin.getLocalization().sendMessage(p, "messages.radiation"); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/CapacitorTextureUpdateTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/CapacitorTextureUpdateTask.java new file mode 100644 index 000000000..b427b0008 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/CapacitorTextureUpdateTask.java @@ -0,0 +1,81 @@ +package io.github.thebusybiscuit.slimefun4.implementation.tasks; + +import javax.annotation.Nonnull; + +import org.apache.commons.lang.Validate; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.block.Block; + +import io.github.thebusybiscuit.cscorelib2.skull.SkullBlock; +import io.github.thebusybiscuit.slimefun4.implementation.items.electric.Capacitor; +import io.github.thebusybiscuit.slimefun4.utils.HeadTexture; +import io.papermc.lib.PaperLib; + +/** + * This task is run whenever a {@link Capacitor} needs to update their texture. + * This must be executed on the main {@link Server} {@link Thread}! + * + * @author TheBusyBiscuit + * + */ +public class CapacitorTextureUpdateTask implements Runnable { + + /** + * The {@link Location} of the {@link Capacitor}. + */ + private final Location l; + + /** + * The level of how "full" this {@link Capacitor} is. + * From 0.0 to 1.0. + */ + private final double filledPercentage; + + /** + * This creates a new {@link CapacitorTextureUpdateTask} with the given parameters. + * + * @param l + * The {@link Location} of the {@link Capacitor} + * @param charge + * The amount of charge in this {@link Capacitor} + * @param capacity + * The capacity of this {@link Capacitor} + */ + public CapacitorTextureUpdateTask(@Nonnull Location l, double charge, double capacity) { + Validate.notNull(l, "The Location cannot be null"); + + this.l = l; + this.filledPercentage = charge / capacity; + } + + @Override + public void run() { + Block b = l.getBlock(); + Material type = b.getType(); + + // Ensure that this Block is still a Player Head + if (type == Material.PLAYER_HEAD || type == Material.PLAYER_WALL_HEAD) { + if (filledPercentage <= 0.25) { + // 0-25% capacity + setTexture(b, HeadTexture.CAPACITOR_25); + } else if (filledPercentage <= 0.5) { + // 25-50% capacity + setTexture(b, HeadTexture.CAPACITOR_50); + } else if (filledPercentage <= 0.75) { + // 50-75% capacity + setTexture(b, HeadTexture.CAPACITOR_75); + } else { + // 75-100% capacity + setTexture(b, HeadTexture.CAPACITOR_100); + } + } + } + + private void setTexture(@Nonnull Block b, @Nonnull HeadTexture texture) { + SkullBlock.setFromHash(b, texture.getUniqueId(), texture.getTexture(), false); + PaperLib.getBlockState(b, false).getState().update(true, false); + } + +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/MagnetTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/InfusedMagnetTask.java similarity index 78% rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/MagnetTask.java rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/InfusedMagnetTask.java index 480041401..f9aed1f42 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/MagnetTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/InfusedMagnetTask.java @@ -13,7 +13,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedMa import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; /** - * This {@link PlayerTask} is run when a {@link Player} carries an {@link InfusedMagnet}. + * This {@link AbstractPlayerTask} is run when a {@link Player} carries an {@link InfusedMagnet}. * It manages the automatic pickup of nearby items. * * @author TheBusyBiscuit @@ -21,12 +21,15 @@ import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; * @see InfusedMagnet * */ -public class MagnetTask extends PlayerTask { +public class InfusedMagnetTask extends AbstractPlayerTask { + /** + * The radius in which an {@link Item} is picked up. + */ private final double radius; /** - * This creates a new {@link MagnetTask} for the given {@link Player} with the given + * This creates a new {@link InfusedMagnetTask} for the given {@link Player} with the given * pickup radius. * * @param p @@ -34,7 +37,7 @@ public class MagnetTask extends PlayerTask { * @param radius * The radius in which items should be picked up */ - public MagnetTask(@Nonnull Player p, double radius) { + public InfusedMagnetTask(@Nonnull Player p, double radius) { super(p); this.radius = radius; @@ -55,6 +58,7 @@ public class MagnetTask extends PlayerTask { } } + // Only play a sound if an Item was found if (playSound) { p.playSound(p.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS, 0.25F, 0.9F); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/JetBootsTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/JetBootsTask.java index 5946d5f8a..9434cbeeb 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/JetBootsTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/JetBootsTask.java @@ -4,7 +4,6 @@ import java.util.concurrent.ThreadLocalRandom; import javax.annotation.Nonnull; -import org.bukkit.Bukkit; import org.bukkit.Effect; import org.bukkit.Material; import org.bukkit.Sound; @@ -14,7 +13,7 @@ import org.bukkit.util.Vector; import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.JetBoots; -public class JetBootsTask extends PlayerTask { +public class JetBootsTask extends AbstractPlayerTask { private static final float COST = 0.075F; @@ -43,7 +42,7 @@ public class JetBootsTask extends PlayerTask { p.setVelocity(vector); } else { - Bukkit.getScheduler().cancelTask(id); + cancel(); } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/JetpackTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/JetpackTask.java index cc0bac58a..0a4a60bcb 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/JetpackTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/JetpackTask.java @@ -2,7 +2,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks; import javax.annotation.Nonnull; -import org.bukkit.Bukkit; import org.bukkit.Effect; import org.bukkit.Material; import org.bukkit.Sound; @@ -11,7 +10,7 @@ import org.bukkit.util.Vector; import io.github.thebusybiscuit.slimefun4.implementation.items.electric.gadgets.Jetpack; -public class JetpackTask extends PlayerTask { +public class JetpackTask extends AbstractPlayerTask { private static final float COST = 0.08F; @@ -22,11 +21,6 @@ public class JetpackTask extends PlayerTask { this.jetpack = jetpack; } - @Override - public void setID(int id) { - this.id = id; - } - @Override protected void executeTask() { if (p.getInventory().getChestplate() == null || p.getInventory().getChestplate().getType() == Material.AIR) { @@ -43,7 +37,7 @@ public class JetpackTask extends PlayerTask { p.setVelocity(vector); } else { - Bukkit.getScheduler().cancelTask(id); + cancel(); } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ParachuteTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ParachuteTask.java index 2693853c2..5d70622de 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ParachuteTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ParachuteTask.java @@ -2,11 +2,22 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks; import javax.annotation.Nonnull; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.util.Vector; -public class ParachuteTask extends PlayerTask { +import io.github.thebusybiscuit.slimefun4.implementation.items.armor.Parachute; + +/** + * The {@link ParachuteTask} adds the entire functionality of the {@link Parachute}. + * It continously sets the velocity of the {@link Player} to make them fall slowly. + * Perhaps it can be changed to use the slow falling effect at some point. + * + * @author TheBusyBiscuit + * + * @see Parachute + * + */ +public class ParachuteTask extends AbstractPlayerTask { public ParachuteTask(@Nonnull Player p) { super(p); @@ -20,7 +31,7 @@ public class ParachuteTask extends PlayerTask { p.setFallDistance(0F); if (!p.isSneaking()) { - Bukkit.getScheduler().cancelTask(id); + cancel(); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/SlimefunStartupTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/SlimefunStartupTask.java index 4b273a842..066a50b45 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/SlimefunStartupTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/SlimefunStartupTask.java @@ -8,8 +8,6 @@ import org.bukkit.Bukkit; import org.bukkit.World; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; -import io.github.thebusybiscuit.slimefun4.implementation.listeners.ButcherAndroidListener; -import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.TeleporterListener; import io.github.thebusybiscuit.slimefun4.implementation.setup.PostSetup; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; @@ -60,19 +58,10 @@ public class SlimefunStartupTask implements Runnable { } } - // Load all listeners that depend on items to be enabled - + // Only load this Listener if the corresponding items are enabled if (isEnabled("ELEVATOR_PLATE", "GPS_ACTIVATION_DEVICE_SHARED", "GPS_ACTIVATION_DEVICE_PERSONAL")) { new TeleporterListener(plugin); } - - if (isEnabled("PROGRAMMABLE_ANDROID_BUTCHER", "PROGRAMMABLE_ANDROID_2_BUTCHER", "PROGRAMMABLE_ANDROID_3_BUTCHER")) { - new ButcherAndroidListener(plugin); - } - - if (isEnabled("ENERGY_REGULATOR", "CARGO_MANAGER")) { - new NetworkListener(plugin, SlimefunPlugin.getNetworkManager()); - } } private boolean isEnabled(String... itemIds) { @@ -83,6 +72,7 @@ public class SlimefunStartupTask implements Runnable { return true; } } + return false; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java index 1b8e09969..11fe81551 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; @@ -10,17 +11,18 @@ import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; +import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.scheduler.BukkitScheduler; import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition; +import io.github.thebusybiscuit.cscorelib2.blocks.ChunkPosition; import io.github.thebusybiscuit.slimefun4.api.ErrorReport; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; -import io.github.thebusybiscuit.slimefun4.utils.PatternUtils; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker; @@ -28,8 +30,8 @@ import me.mrCookieSlime.Slimefun.api.BlockStorage; import me.mrCookieSlime.Slimefun.api.Slimefun; /** - * The {@link TickerTask} is responsible for ticking every {@link BlockTicker}, synchronous - * or not. + * The {@link TickerTask} is responsible for ticking every {@link BlockTicker}, + * synchronous or not. * * @author TheBusyBiscuit * @@ -38,15 +40,19 @@ import me.mrCookieSlime.Slimefun.api.Slimefun; */ public class TickerTask implements Runnable { - // This Map holds all currently actively ticking locations - private final Map> activeTickers = new ConcurrentHashMap<>(); + /** + * This Map holds all currently actively ticking locations. + */ + private final Map> tickingLocations = new ConcurrentHashMap<>(); // These are "Queues" of blocks that need to be removed or moved private final Map movingQueue = new ConcurrentHashMap<>(); private final Map deletionQueue = new ConcurrentHashMap<>(); - // This Map tracks how many bugs have occurred in a given Location - // If too many bugs happen, we delete that Location + /** + * This Map tracks how many bugs have occurred in a given Location . + * If too many bugs happen, we delete that Location. + */ private final Map bugs = new ConcurrentHashMap<>(); private int tickRate; @@ -69,7 +75,7 @@ public class TickerTask implements Runnable { /** * This method resets this {@link TickerTask} to run again. */ - public void reset() { + private void reset() { running = false; } @@ -85,6 +91,7 @@ public class TickerTask implements Runnable { SlimefunPlugin.getProfiler().start(); Set tickers = new HashSet<>(); + // Remove any deleted blocks Iterator> removals = deletionQueue.entrySet().iterator(); while (removals.hasNext()) { Map.Entry entry = removals.next(); @@ -92,12 +99,24 @@ public class TickerTask implements Runnable { removals.remove(); } - if (!halted) { - for (Map.Entry> entry : activeTickers.entrySet()) { - tickChunk(tickers, entry.getKey(), entry.getValue()); + // Fixes #2576 - Remove any deleted instances of BlockStorage + Iterator worlds = SlimefunPlugin.getRegistry().getWorlds().values().iterator(); + while (worlds.hasNext()) { + BlockStorage storage = worlds.next(); + + if (storage.isMarkedForRemoval()) { + worlds.remove(); } } + // Run our ticker code + if (!halted) { + for (Map.Entry> entry : tickingLocations.entrySet()) { + tickChunk(entry.getKey(), tickers, entry.getValue()); + } + } + + // Move any moved block data Iterator> moves = movingQueue.entrySet().iterator(); while (moves.hasNext()) { Map.Entry entry = moves.next(); @@ -119,21 +138,16 @@ public class TickerTask implements Runnable { } @ParametersAreNonnullByDefault - private void tickChunk(Set tickers, String chunk, Set locations) { + private void tickChunk(ChunkPosition chunk, Set tickers, Set locations) { try { - String[] components = PatternUtils.SEMICOLON.split(chunk); - - World world = Bukkit.getWorld(components[0]); - int x = Integer.parseInt(components[components.length - 2]); - int z = Integer.parseInt(components[components.length - 1]); - - if (world != null && world.isChunkLoaded(x, z)) { + // Only continue if the Chunk is actually loaded + if (chunk.isLoaded()) { for (Location l : locations) { tickLocation(tickers, l); } } } catch (ArrayIndexOutOfBoundsException | NumberFormatException x) { - Slimefun.getLogger().log(Level.SEVERE, x, () -> "An Exception has occurred while trying to parse Chunk: " + chunk); + Slimefun.getLogger().log(Level.SEVERE, x, () -> "An Exception has occurred while trying to resolve Chunk: " + chunk); } } @@ -146,8 +160,11 @@ public class TickerTask implements Runnable { if (item.getBlockTicker().isSynchronized()) { SlimefunPlugin.getProfiler().scheduleEntries(1); item.getBlockTicker().update(); - // We are inserting a new timestamp because synchronized - // actions are always ran with a 50ms delay (1 game tick) + + /** + * We are inserting a new timestamp because synchronized actions + * are always ran with a 50ms delay (1 game tick) + */ SlimefunPlugin.runSync(() -> { Block b = l.getBlock(); tickBlock(l, b, item, data, System.nanoTime()); @@ -210,14 +227,36 @@ public class TickerTask implements Runnable { @ParametersAreNonnullByDefault public void queueMove(Location from, Location to) { + Validate.notNull(from, "Source Location cannot be null!"); + Validate.notNull(to, "Target Location cannot be null!"); + movingQueue.put(from, to); } @ParametersAreNonnullByDefault public void queueDelete(Location l, boolean destroy) { + Validate.notNull(l, "Location must not be null!"); + deletionQueue.put(l, destroy); } + /** + * This method checks if the given {@link Location} has been reserved + * by this {@link TickerTask}. + * A reserved {@link Location} does not currently hold any data but will + * be occupied upon the next tick. + * Checking this ensures that our {@link Location} does not get treated like a normal + * {@link Location} as it is theoretically "moving". + * + * @param l + * The {@link Location} to check + * + * @return Whether this {@link Location} has been reserved and will be filled upon the next tick + */ + public boolean isReserved(@Nonnull Location l) { + return movingQueue.containsValue(l); + } + /** * This returns the delay between ticks * @@ -228,14 +267,82 @@ public class TickerTask implements Runnable { } /** - * This method returns the {@link Map} of actively ticking locations according to - * their chunk id. + * This method returns a read-only {@link Map} + * representation of every {@link ChunkPosition} and its corresponding + * {@link Set} of ticking {@link Location Locations}. * - * @return The {@link Map} of active tickers + * This does include any {@link Location} from an unloaded {@link Chunk} too! + * + * @return A {@link Map} representation of all ticking {@link Location Locations} */ @Nonnull - public Map> getActiveTickers() { - return activeTickers; + public Map> getLocations() { + return Collections.unmodifiableMap(tickingLocations); + } + + /** + * This method returns a read-only {@link Set} + * of all ticking {@link Location Locations} in a given {@link Chunk}. + * The {@link Chunk} does not have to be loaded. + * If no {@link Location} is present, the returned {@link Set} will be empty. + * + * @param chunk + * The {@link Chunk} + * + * @return A {@link Set} of all ticking {@link Location Locations} + */ + @Nonnull + public Set getLocations(@Nonnull Chunk chunk) { + Validate.notNull(chunk, "The Chunk cannot be null!"); + + Set locations = tickingLocations.getOrDefault(new ChunkPosition(chunk), new HashSet<>()); + return Collections.unmodifiableSet(locations); + } + + /** + * This enables the ticker at the given {@link Location} and adds it to our "queue". + * + * @param l + * The {@link Location} to activate + */ + public void enableTicker(@Nonnull Location l) { + Validate.notNull(l, "Location cannot be null!"); + + ChunkPosition chunk = new ChunkPosition(l.getWorld(), l.getBlockX() >> 4, l.getBlockZ() >> 4); + Set newValue = new HashSet<>(); + Set oldValue = tickingLocations.putIfAbsent(chunk, newValue); + + /** + * This is faster than doing computeIfAbsent(...) + * on a ConcurrentHashMap because it won't block the Thread for too long + */ + if (oldValue != null) { + oldValue.add(l); + } else { + newValue.add(l); + } + } + + /** + * This method disables the ticker at the given {@link Location} and removes it from our internal + * "queue". + * + * @param l + * The {@link Location} to remove + */ + public void disableTicker(@Nonnull Location l) { + Validate.notNull(l, "Location cannot be null!"); + + ChunkPosition chunk = new ChunkPosition(l.getWorld(), l.getBlockX() >> 4, l.getBlockZ() >> 4); + Set locations = tickingLocations.get(chunk); + + if (locations != null) { + locations.remove(l); + + if (locations.isEmpty()) { + tickingLocations.remove(chunk); + } + } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChatUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChatUtils.java index fe1f75b28..333deeb80 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChatUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChatUtils.java @@ -65,6 +65,7 @@ public final class ChatUtils { * * @return A human-friendly version of the given {@link String} */ + @Nonnull public static String humanize(@Nonnull String string) { StringBuilder builder = new StringBuilder(); String[] segments = PatternUtils.UNDERSCORE.split(string.toLowerCase(Locale.ROOT)); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterial.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterial.java index bb7836767..fb791fda3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterial.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterial.java @@ -159,6 +159,9 @@ public enum ColoredMaterial { // @formatter:on + /** + * This is our {@link List} of {@link Material Materials}, the backbone of this enum. + */ private final List list; /** @@ -175,11 +178,25 @@ public enum ColoredMaterial { list = Collections.unmodifiableList(Arrays.asList(materials)); } + /** + * This returns an ordered {@link List} of {@link Material Materials} + * that are part o this {@link ColoredMaterial}. + * + * @return An ordered {@link List} of {@link Material Materials} + */ @Nonnull public List asList() { return list; } + /** + * This returns the {@link Material} at the given index. + * + * @param index + * The index + * + * @return The {@link Material} at that index + */ @Nonnull public Material get(int index) { Validate.isTrue(index >= 0 && index < 16, "The index must be between 0 and 15 (inclusive)."); @@ -187,6 +204,15 @@ public enum ColoredMaterial { return list.get(index); } + /** + * This returns the {@link Material} with the given {@link DyeColor}. + * + * @param color + * The {@link DyeColor} + * + * @return The {@link Material} with that {@link DyeColor} + */ + @Nonnull public Material get(@Nonnull DyeColor color) { Validate.notNull(color, "Color cannot be null!"); 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 ee8a849d0..ce1b5b7c7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/HeadTexture.java @@ -1,5 +1,8 @@ package io.github.thebusybiscuit.slimefun4.utils; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + import javax.annotation.Nonnull; import org.apache.commons.lang.Validate; @@ -114,11 +117,14 @@ public enum HeadTexture { public static final HeadTexture[] valuesCache = values(); private final String texture; + private final UUID uuid; HeadTexture(@Nonnull String texture) { Validate.notNull(texture, "Texture cannot be null"); Validate.isTrue(PatternUtils.HEXADECIMAL.matcher(texture).matches(), "Textures must be in hexadecimal."); + this.texture = texture; + this.uuid = UUID.nameUUIDFromBytes(texture.getBytes(StandardCharsets.UTF_8)); } /** @@ -131,6 +137,18 @@ public enum HeadTexture { return texture; } + /** + * This returns the {@link UUID} for this {@link HeadTexture}. + * The {@link UUID} is generated from the texture and cached for + * performance reasons. + * + * @return The {@link UUID} for this {@link HeadTexture} + */ + @Nonnull + public UUID getUniqueId() { + return uuid; + } + /** * This method returns an {@link ItemStack} with the given texture assigned to it. * diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java index d752b5ec8..e4e23bfc2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java @@ -24,6 +24,7 @@ public final class NumberUtils { /** * This is our {@link DecimalFormat} for decimal values. + * This instance is not thread-safe! */ private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##", DecimalFormatSymbols.getInstance(Locale.ROOT)); @@ -48,6 +49,34 @@ public final class NumberUtils { return NumberFormat.getNumberInstance(Locale.US).format(number); } + @Nonnull + public static String getCompactDouble(double value) { + if (value < 0) { + // Negative numbers are a special case + return '-' + getCompactDouble(-value); + } + + if (value < 1000.0) { + // Below 1K + return DECIMAL_FORMAT.format(value); + } else if (value < 1000000.0) { + // Thousands + return DECIMAL_FORMAT.format(value / 1000.0) + 'K'; + } else if (value < 1000000000.0) { + // Million + return DECIMAL_FORMAT.format(value / 1000000.0) + 'M'; + } else if (value < 1000000000000.0) { + // Billion + return DECIMAL_FORMAT.format(value / 1000000000.0) + 'B'; + } else if (value < 1000000000000000.0) { + // Trillion + return DECIMAL_FORMAT.format(value / 1000000000000.0) + 'T'; + } else { + // Quadrillion + return DECIMAL_FORMAT.format(value / 1000000000000000.0) + 'Q'; + } + } + /** * This method transforms a String representation of a {@link LocalDateTime} * from GitHub's API back into a {@link LocalDateTime} object @@ -119,18 +148,19 @@ public final class NumberUtils { * One hour later it will read {@code "1d 1h"}. For values smaller than an hour {@code "< 1h"} * will be returned instead. * - * @param start - * The starting {@link LocalDateTime}. - * @param end - * The ending {@link LocalDateTime}. + * @param current + * The current {@link LocalDateTime}. + * @param priorDate + * The {@link LocalDateTime} in the past. * * @return The elapsed time as a {@link String} */ @Nonnull - public static String getElapsedTime(@Nonnull LocalDateTime start, @Nonnull LocalDateTime end) { - Validate.notNull(start, "Provided start was null"); - Validate.notNull(end, "Provided end was null"); - long hours = Duration.between(start, end).toHours(); + public static String getElapsedTime(@Nonnull LocalDateTime current, @Nonnull LocalDateTime priorDate) { + Validate.notNull(current, "Provided current date was null"); + Validate.notNull(priorDate, "Provided past date was null"); + + long hours = Duration.between(priorDate, current).toHours(); if (hours == 0) { return "< 1h"; 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 05046d5d8..2369a6898 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java @@ -14,7 +14,6 @@ import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.block.Block; import org.bukkit.entity.Item; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; @@ -24,7 +23,6 @@ import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; import io.github.thebusybiscuit.cscorelib2.item.ImmutableItemMeta; -import io.github.thebusybiscuit.cscorelib2.skull.SkullBlock; import io.github.thebusybiscuit.cscorelib2.skull.SkullItem; import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion; import io.github.thebusybiscuit.slimefun4.api.exceptions.PrematureCodeException; @@ -32,9 +30,8 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactive; import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal; +import io.github.thebusybiscuit.slimefun4.implementation.tasks.CapacitorTextureUpdateTask; import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper; -import me.mrCookieSlime.EmeraldEnchants.EmeraldEnchants; -import me.mrCookieSlime.EmeraldEnchants.ItemEnchantment; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; @@ -49,7 +46,6 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; */ public final class SlimefunUtils { - private static final String EMERALDENCHANTS_LORE = ChatColor.YELLOW.toString() + ChatColor.YELLOW.toString() + ChatColor.GRAY.toString(); private static final String NO_PICKUP_METADATA = "no_pickup"; private static final NamespacedKey SOULBOUND_KEY = new NamespacedKey(SlimefunPlugin.instance(), "soulbound"); @@ -99,15 +95,6 @@ public final class SlimefunUtils { return true; } - if (SlimefunPlugin.getThirdPartySupportService().isEmeraldEnchantsInstalled()) { - // We wanna operate on a copy now - item = item.clone(); - - for (ItemEnchantment enchantment : EmeraldEnchants.getInstance().getRegistry().getEnchantments(item)) { - EmeraldEnchants.getInstance().getRegistry().applyEnchantment(item, enchantment.getEnchantment(), 0); - } - } - SlimefunItem sfItem = SlimefunItem.getByItem(item); if (sfItem instanceof Soulbound) { @@ -314,46 +301,61 @@ public final class SlimefunUtils { } } - private static boolean equalsLore(@Nonnull List lore, @Nonnull List lore2) { - StringBuilder string1 = new StringBuilder(); - StringBuilder string2 = new StringBuilder(); + /** + * This checks if the two provided lores are equal. + * This method will ignore any lines such as the soulbound one. + * + * @param lore1 + * The first lore + * @param lore2 + * The second lore + * + * @return Whether the two lores are equal + */ + public static boolean equalsLore(@Nonnull List lore1, @Nonnull List lore2) { + Validate.notNull(lore1, "Cannot compare lore that is null!"); + Validate.notNull(lore2, "Cannot compare lore that is null!"); - for (String string : lore) { - if (!string.equals(SOULBOUND_LORE) && !string.startsWith(EMERALDENCHANTS_LORE)) { - string1.append("-NEW LINE-").append(string); + List longerList = lore1.size() > lore2.size() ? lore1 : lore2; + List shorterList = lore1.size() > lore2.size() ? lore2 : lore1; + + int a = 0; + int b = 0; + + for (; a < longerList.size(); a++) { + if (isLineIgnored(longerList.get(a))) { + continue; + } + + while (shorterList.size() > b && isLineIgnored(shorterList.get(b))) { + b++; + } + + if (b >= shorterList.size()) { + return false; + } else if (longerList.get(a).equals(shorterList.get(b))) { + b++; + } else { + return false; } } - for (String string : lore2) { - if (!string.equals(SOULBOUND_LORE) && !string.startsWith(EMERALDENCHANTS_LORE)) { - string2.append("-NEW LINE-").append(string); - } + while (shorterList.size() > b && isLineIgnored(shorterList.get(b))) { + b++; } - return string1.toString().equals(string2.toString()); + return b == shorterList.size(); + } + + private static boolean isLineIgnored(@Nonnull String line) { + return line.equals(SOULBOUND_LORE); } public static void updateCapacitorTexture(@Nonnull Location l, int charge, int capacity) { Validate.notNull(l, "Cannot update a texture for null"); Validate.isTrue(capacity > 0, "Capacity must be greater than zero!"); - SlimefunPlugin.runSync(() -> { - Block b = l.getBlock(); - - if (b.getType() == Material.PLAYER_HEAD || b.getType() == Material.PLAYER_WALL_HEAD) { - double level = (double) charge / capacity; - - if (level <= 0.25) { - SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_25.getTexture()); - } else if (level <= 0.5) { - SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_50.getTexture()); - } else if (level <= 0.75) { - SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_75.getTexture()); - } else { - SkullBlock.setFromHash(b, HeadTexture.CAPACITOR_100.getTexture()); - } - } - }); + SlimefunPlugin.runSync(new CapacitorTextureUpdateTask(l, charge, capacity)); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/holograms/ReactorHologram.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/holograms/ReactorHologram.java index a0d56e4bf..fc2762c16 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/holograms/ReactorHologram.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/holograms/ReactorHologram.java @@ -1,5 +1,7 @@ package io.github.thebusybiscuit.slimefun4.utils.holograms; +import java.util.Collection; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -17,9 +19,10 @@ public final class ReactorHologram { @Nullable public static ArmorStand getArmorStand(@Nonnull Location reactor, boolean createIfNoneExists) { Location l = new Location(reactor.getWorld(), reactor.getX() + 0.5, reactor.getY() + 0.7, reactor.getZ() + 0.5); + Collection holograms = l.getWorld().getNearbyEntities(l, 0.2, 0.2, 0.2, ReactorHologram::isPossibleHologram); - for (Entity n : l.getChunk().getEntities()) { - if (n instanceof ArmorStand && l.distanceSquared(n.getLocation()) < 0.4D) { + for (Entity n : holograms) { + if (n instanceof ArmorStand) { return (ArmorStand) n; } } @@ -34,14 +37,21 @@ public final class ReactorHologram { return hologram; } + private static boolean isPossibleHologram(@Nonnull Entity n) { + if (n instanceof ArmorStand) { + ArmorStand armorstand = (ArmorStand) n; + return armorstand.isValid() && armorstand.isSilent() && armorstand.isMarker() && !armorstand.hasGravity(); + } else { + return false; + } + + } + public static void update(@Nonnull Location l, @Nonnull String name) { SlimefunPlugin.runSync(() -> { ArmorStand hologram = getArmorStand(l, true); - if (!hologram.isCustomNameVisible()) { - hologram.setCustomNameVisible(true); - } - + hologram.setCustomNameVisible(true); hologram.setCustomName(ChatColors.color(name)); }); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/holograms/SimpleHologram.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/holograms/SimpleHologram.java index f31fce8c1..84f0f01ce 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/holograms/SimpleHologram.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/holograms/SimpleHologram.java @@ -1,5 +1,7 @@ package io.github.thebusybiscuit.slimefun4.utils.holograms; +import java.util.Collection; + import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -42,9 +44,10 @@ public final class SimpleHologram { @Nullable private static ArmorStand getArmorStand(@Nonnull Block b, boolean createIfNoneExists) { Location l = new Location(b.getWorld(), b.getX() + 0.5, b.getY() + 0.7F, b.getZ() + 0.5); + Collection holograms = b.getWorld().getNearbyEntities(l, 0.2, 0.2, 0.2, SimpleHologram::isPossibleHologram); - for (Entity n : l.getChunk().getEntities()) { - if (n instanceof ArmorStand && l.distanceSquared(n.getLocation()) < 0.4D && isPossibleHologram((ArmorStand) n)) { + for (Entity n : holograms) { + if (n instanceof ArmorStand) { return (ArmorStand) n; } } @@ -56,8 +59,14 @@ public final class SimpleHologram { } } - private static boolean isPossibleHologram(@Nonnull ArmorStand armorstand) { - return armorstand.isValid() && armorstand.isSilent() && armorstand.isMarker() && !armorstand.hasGravity() && armorstand.isCustomNameVisible(); + private static boolean isPossibleHologram(@Nonnull Entity n) { + if (n instanceof ArmorStand) { + ArmorStand armorstand = (ArmorStand) n; + return armorstand.isValid() && armorstand.isSilent() && armorstand.isMarker() && !armorstand.hasGravity() && armorstand.isCustomNameVisible(); + } else { + return false; + } + } @Nonnull diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/itemstack/ItemStackWrapper.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/itemstack/ItemStackWrapper.java index a910908ae..db31d825d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/itemstack/ItemStackWrapper.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/itemstack/ItemStackWrapper.java @@ -42,6 +42,10 @@ public final class ItemStackWrapper extends ItemStack { } } + public ItemStackWrapper(@Nonnull Material material) { + this(new ItemStack(material)); + } + @Override public boolean hasItemMeta() { return hasItemMeta; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/tags/SlimefunTag.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/tags/SlimefunTag.java index 289314298..1a7869875 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/tags/SlimefunTag.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/tags/SlimefunTag.java @@ -62,6 +62,11 @@ public enum SlimefunTag implements Tag { * All command block variants */ COMMAND_BLOCKS, + + /** + * All variants of Spawn Eggs + */ + SPAWN_EGGS, /** * Every mushroom type, red, brown and nether ones. @@ -300,6 +305,18 @@ public enum SlimefunTag implements Tag { } } + public boolean isEmpty() { + if (!includedMaterials.isEmpty()) { + /** + * Without even needing to generate a Set we can safely + * return false if there are directly included Materials + */ + return false; + } else { + return getValues().isEmpty(); + } + } + /** * This returns a {@link Set} of {@link Tag Tags} which are children of this {@link SlimefunTag}, * these can be other {@link SlimefunTag SlimefunTags} or regular {@link Tag Tags}. @@ -347,6 +364,7 @@ public enum SlimefunTag implements Tag { @Nullable public static SlimefunTag getTag(@Nonnull String value) { Validate.notNull(value, "A tag cannot be null!"); + return nameLookup.get(value); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/tags/TagParser.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/tags/TagParser.java index 6b9f10792..89bb7b025 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/tags/TagParser.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/tags/TagParser.java @@ -106,8 +106,10 @@ public class TagParser implements Keyed { // Strings will be parsed directly parsePrimitiveValue(element.getAsString(), materials, tags, true); } else if (element instanceof JsonObject) { - // JSONObjects can have a "required" property which can make - // it optional to resolve the underlying value + /** + * JSONObjects can have a "required" property which can + * make it optional to resolve the underlying value + */ parseComplexValue(element.getAsJsonObject(), materials, tags); } else { throw new TagMisconfigurationException(key, "Unexpected value format: " + element.getClass().getSimpleName() + " - " + element.toString()); @@ -179,8 +181,10 @@ public class TagParser implements Keyed { if (id instanceof JsonPrimitive && ((JsonPrimitive) id).isString() && required instanceof JsonPrimitive && ((JsonPrimitive) required).isBoolean()) { boolean isRequired = required.getAsBoolean(); - // If the Tag is required, an exception may be thrown. - // Otherwise it will just ignore the value + /** + * If the Tag is required, an exception may be thrown. + * Otherwise it will just ignore the value + */ parsePrimitiveValue(id.getAsString(), materials, tags, isRequired); } else { throw new TagMisconfigurationException(key, "Found a JSON Object value without an id!"); 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 8facdbc35..666f8dae4 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/SlimefunItem.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/SlimefunItem.java @@ -719,15 +719,11 @@ public class SlimefunItem implements Placeable { * This method is used for internal purposes only. */ public void load() { - try { - if (!hidden) { - category.add(this); - } - - recipeType.register(recipe, getRecipeOutput()); - } catch (Exception x) { - error("Failed to properly load the Item \"" + id + "\"", x); + if (!hidden) { + category.add(this); } + + recipeType.register(recipe, getRecipeOutput()); } /** @@ -983,6 +979,20 @@ public class SlimefunItem implements Placeable { } } + @Override + public final boolean equals(Object obj) { + if (obj instanceof SlimefunItem) { + return ((SlimefunItem) obj).getId().equals(getId()); + } else { + return false; + } + } + + @Override + public final int hashCode() { + return getId().hashCode(); + } + @Nullable public static SlimefunItem getByID(@Nonnull String id) { return SlimefunPlugin.getRegistry().getSlimefunItemIds().get(id); @@ -1032,4 +1042,5 @@ public class SlimefunItem implements Placeable { public static void registerBlockHandler(String id, SlimefunBlockHandler handler) { SlimefunPlugin.getRegistry().getBlockHandlers().put(id, handler); } + } 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 964db45ec..2572751b1 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 @@ -252,6 +252,8 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock, * identify all instances of the same {@link AContainer}. * This way we can add the recipes to all instances of the same machine. * + * This method will be deprecated and replaced in the future + * * @return The identifier of this machine */ @Nonnull @@ -384,11 +386,13 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock, Validate.notNull(l, "Can't attempt to take charge from a null location!"); if (isChargeable()) { - if (getCharge(l) < getEnergyConsumption()) { + int charge = getCharge(l); + + if (charge < getEnergyConsumption()) { return false; } - removeCharge(l, getEnergyConsumption()); + setCharge(l, charge - getEnergyConsumption()); return true; } else { return true; 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 88f977920..0ae282211 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 @@ -63,7 +63,7 @@ public abstract class AGenerator extends AbstractEnergyProvider { @Override public boolean canOpen(Block b, Player p) { - return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.ACCESS_INVENTORIES); + return p.hasPermission("slimefun.inventory.bypass") || SlimefunPlugin.getProtectionManager().hasPermission(p, b.getLocation(), ProtectableAction.INTERACT_BLOCK); } @Override @@ -152,7 +152,7 @@ public abstract class AGenerator extends AbstractEnergyProvider { ChestMenuUtils.updateProgressbar(inv, 22, timeleft, processing.get(l).getTicks(), getProgressBar()); if (isChargeable()) { - int charge = getCharge(l); + int charge = getCharge(l, data); if (getCapacity() - charge >= getEnergyProduction()) { progress.put(l, timeleft - 1); @@ -202,7 +202,7 @@ public abstract class AGenerator extends AbstractEnergyProvider { } ItemStackWrapper wrapper = new ItemStackWrapper(item); - return SlimefunUtils.isItemSimilar(wrapper, new ItemStack(Material.LAVA_BUCKET), true) || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.FUEL_BUCKET, true) || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.OIL_BUCKET, true); + return item.getType() == Material.LAVA_BUCKET || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.FUEL_BUCKET, true) || SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.OIL_BUCKET, true); } private MachineFuel findRecipe(BlockMenu menu, Map found) { diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/interfaces/InventoryBlock.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/interfaces/InventoryBlock.java index 8de896dc1..b09f898e8 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/interfaces/InventoryBlock.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/interfaces/InventoryBlock.java @@ -19,7 +19,6 @@ import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; * @deprecated This interface is not designed to be used by addons. * */ -@Deprecated public interface InventoryBlock { /** diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/ItemHandler.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/ItemHandler.java index ca6417ae4..7333724c2 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/ItemHandler.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/ItemHandler.java @@ -47,6 +47,7 @@ public interface ItemHandler { * * @param item * The {@link SlimefunItem} to validate + * * @return An {@link Optional} describing the result, it will contain an {@link IncompatibleItemHandlerException} * should there be an issue */ diff --git a/src/main/java/me/mrCookieSlime/Slimefun/api/BlockInfoConfig.java b/src/main/java/me/mrCookieSlime/Slimefun/api/BlockInfoConfig.java index 8bc5dc5de..e34351800 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/api/BlockInfoConfig.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/api/BlockInfoConfig.java @@ -49,8 +49,6 @@ public class BlockInfoConfig extends Config { throw new UnsupportedOperationException("Can't set \"" + path + "\" to \"" + value + "\" (type: " + value.getClass().getSimpleName() + ") because BlockInfoConfig only supports Strings"); } - checkPath(path); - if (value == null) { data.remove(path); } else { @@ -58,15 +56,8 @@ public class BlockInfoConfig extends Config { } } - private void checkPath(String path) { - if (path.indexOf('.') != -1) { - throw new UnsupportedOperationException("BlockInfoConfig only supports Map (path: " + path + ")"); - } - } - @Override public boolean contains(String path) { - checkPath(path); return data.containsKey(path); } @@ -77,7 +68,6 @@ public class BlockInfoConfig extends Config { @Override public String getString(String path) { - checkPath(path); return data.get(path); } diff --git a/src/main/java/me/mrCookieSlime/Slimefun/api/BlockStorage.java b/src/main/java/me/mrCookieSlime/Slimefun/api/BlockStorage.java index 94b7a2297..c8de41284 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/api/BlockStorage.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/api/BlockStorage.java @@ -7,11 +7,10 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,7 +32,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.stream.JsonWriter; -import io.github.thebusybiscuit.cscorelib2.blocks.BlockPosition; import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.utils.PatternUtils; @@ -58,24 +56,30 @@ public class BlockStorage { private final Map blocksCache = new ConcurrentHashMap<>(); private static int chunkChanges = 0; - private int changes = 0; + private int changes = 0; + private AtomicBoolean isMarkedForRemoval = new AtomicBoolean(false); + + @Nullable public static BlockStorage getStorage(@Nonnull World world) { return SlimefunPlugin.getRegistry().getWorlds().get(world.getName()); } - public static BlockStorage getForcedStorage(@Nonnull World world) { - return isWorldRegistered(world.getName()) ? SlimefunPlugin.getRegistry().getWorlds().get(world.getName()) : new BlockStorage(world); + @Nonnull + public static BlockStorage getOrCreate(@Nonnull World world) { + BlockStorage storage = SlimefunPlugin.getRegistry().getWorlds().get(world.getName()); + + if (storage == null) { + return new BlockStorage(world); + } else { + return storage; + } } private static String serializeLocation(Location l) { return l.getWorld().getName() + ';' + l.getBlockX() + ';' + l.getBlockY() + ';' + l.getBlockZ(); } - private static String locationToChunkString(Location l) { - return l.getWorld().getName() + ";Chunk;" + (l.getBlockX() >> 4) + ';' + (l.getBlockZ() >> 4); - } - private static String serializeChunk(World world, int x, int z) { return world.getName() + ";Chunk;" + x + ';' + z; } @@ -178,7 +182,6 @@ public class BlockStorage { } try { - String chunkString = locationToChunkString(l); String json = cfg.getString(key); Config blockInfo = parseBlockInfo(l, json); @@ -196,9 +199,7 @@ public class BlockStorage { storage.put(l, blockInfo); if (SlimefunPlugin.getRegistry().getTickerBlocks().contains(file.getName().replace(".sfb", ""))) { - Map> tickers = SlimefunPlugin.getTickerTask().getActiveTickers(); - Set locations = tickers.computeIfAbsent(chunkString, id -> new HashSet<>()); - locations.add(l); + SlimefunPlugin.getTickerTask().enableTicker(l); } } } catch (Exception x) { @@ -331,7 +332,11 @@ public class BlockStorage { public void saveAndRemove() { save(); - SlimefunPlugin.getRegistry().getWorlds().remove(world.getName()); + isMarkedForRemoval.set(true); + } + + public boolean isMarkedForRemoval() { + return isMarkedForRemoval.get(); } public static void saveChunks() { @@ -496,11 +501,7 @@ public class BlockStorage { } } - public static void setBlockInfo(Block block, Config cfg, boolean updateTicker) { - setBlockInfo(block.getLocation(), cfg, updateTicker); - } - - public static void setBlockInfo(Location l, Config cfg, boolean updateTicker) { + private static void setBlockInfo(Location l, Config cfg, boolean updateTicker) { BlockStorage storage = getStorage(l.getWorld()); if (storage == null) { @@ -510,15 +511,13 @@ public class BlockStorage { storage.storage.put(l, cfg); String id = cfg.getString("id"); + BlockMenuPreset preset = BlockMenuPreset.getPreset(id); - if (BlockMenuPreset.isInventory(id)) { + if (preset != null) { if (BlockMenuPreset.isUniversalInventory(id)) { - if (!SlimefunPlugin.getRegistry().getUniversalInventories().containsKey(id)) { - storage.loadUniversalInventory(BlockMenuPreset.getPreset(id)); - } + SlimefunPlugin.getRegistry().getUniversalInventories().computeIfAbsent(id, key -> new UniversalBlockMenu(preset)); } else if (!storage.hasInventory(l)) { File file = new File(PATH_INVENTORIES + serializeLocation(l) + ".sfi"); - BlockMenuPreset preset = BlockMenuPreset.getPreset(id); if (file.exists()) { BlockMenu inventory = new BlockMenu(preset, l, new io.github.thebusybiscuit.cscorelib2.config.Config(file)); @@ -574,6 +573,10 @@ public class BlockStorage { public static void deleteLocationInfoUnsafely(Location l, boolean destroy) { BlockStorage storage = getStorage(l.getWorld()); + if (storage == null) { + throw new IllegalStateException("World \"" + l.getWorld().getName() + "\" seems to have been deleted. Do not call unsafe methods directly!"); + } + if (hasBlockInfo(l)) { refreshCache(storage, l, getLocationInfo(l).getString("id"), null, destroy); storage.storage.remove(l); @@ -591,17 +594,7 @@ public class BlockStorage { universalInventory.save(); } - String chunkString = locationToChunkString(l); - Map> tickers = SlimefunPlugin.getTickerTask().getActiveTickers(); - Set locations = tickers.get(chunkString); - - if (locations != null) { - locations.remove(l); - - if (locations.isEmpty()) { - tickers.remove(chunkString); - } - } + SlimefunPlugin.getTickerTask().disableTicker(l); } } @@ -639,23 +632,15 @@ public class BlockStorage { refreshCache(storage, from, previousData.getString("id"), null, true); storage.storage.remove(from); - String chunkString = locationToChunkString(from); - Map> tickers = SlimefunPlugin.getTickerTask().getActiveTickers(); - Set locations = tickers.get(chunkString); - - if (locations != null) { - locations.remove(from); - - if (locations.isEmpty()) { - tickers.remove(chunkString); - } - } + SlimefunPlugin.getTickerTask().disableTicker(from); } private static void refreshCache(BlockStorage storage, Location l, String key, String value, boolean updateTicker) { if (key == null) { - // This Block is no longer valid... - // Fixes #1577 + /** + * This Block is no longer valid... + * Fixes #1577 + */ return; } @@ -665,31 +650,26 @@ public class BlockStorage { if (updateTicker) { SlimefunItem item = SlimefunItem.getByID(key); - if (item != null && item.isTicking()) { - String chunkString = locationToChunkString(l); - - if (value != null) { - Map> tickers = SlimefunPlugin.getTickerTask().getActiveTickers(); - Set locations = tickers.computeIfAbsent(chunkString, id -> new HashSet<>()); - locations.add(l); - } + if (item != null && item.isTicking() && value != null) { + SlimefunPlugin.getTickerTask().enableTicker(l); } } } - public static SlimefunItem check(Block block) { - return check(block.getLocation()); + @Nullable + public static SlimefunItem check(@Nonnull Block b) { + String id = checkID(b); + return id == null ? null : SlimefunItem.getByID(id); } - public static SlimefunItem check(Location l) { - if (!hasBlockInfo(l)) { - return null; - } - - return SlimefunItem.getByID(getLocationInfo(l, "id")); + @Nullable + public static SlimefunItem check(@Nonnull Location l) { + String id = checkID(l); + return id == null ? null : SlimefunItem.getByID(id); } - public static String checkID(Block b) { + @Nullable + public static String checkID(@Nonnull Block b) { if (SlimefunPlugin.getBlockDataService().isTileEntity(b.getType())) { Optional blockData = SlimefunPlugin.getBlockDataService().getBlockData(b); @@ -714,22 +694,17 @@ public class BlockStorage { return getLocationInfo(l, "id"); } - public static boolean check(Location l, String slimefunItem) { - if (slimefunItem == null || !hasBlockInfo(l)) { + public static boolean check(@Nonnull Location l, @Nullable String slimefunItem) { + if (slimefunItem == null) { return false; } - try { - String id = getLocationInfo(l, "id"); - return id != null && id.equalsIgnoreCase(slimefunItem); - } catch (Exception x) { - Slimefun.getLogger().log(Level.SEVERE, x, () -> "An Exception occurred while checking " + new BlockPosition(l) + " for: \"" + slimefunItem + "\""); - return false; - } + String id = checkID(l); + return id != null && id.equals(slimefunItem); } - public static boolean isWorldRegistered(String name) { - return SlimefunPlugin.getRegistry().getWorlds().containsKey(name); + public static boolean isWorldLoaded(World world) { + return SlimefunPlugin.getRegistry().getWorlds().containsKey(world.getName()); } public BlockMenu loadInventory(Location l, BlockMenuPreset preset) { @@ -757,11 +732,6 @@ public class BlockStorage { } } - public void loadUniversalInventory(BlockMenuPreset preset) { - UniversalBlockMenu inventory = new UniversalBlockMenu(preset); - SlimefunPlugin.getRegistry().getUniversalInventories().put(preset.getID(), inventory); - } - public void clearInventory(Location l) { BlockMenu menu = getInventory(l); @@ -830,7 +800,7 @@ public class BlockStorage { public static Config getChunkInfo(World world, int x, int z) { try { - if (!isWorldRegistered(world.getName())) { + if (!isWorldLoaded(world)) { return emptyBlockData; } diff --git a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java index 4c80fedb2..3239d8f19 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java @@ -300,7 +300,7 @@ public abstract class BlockMenuPreset extends ChestMenu { } @Nullable - public static BlockMenuPreset getPreset(String id) { + public static BlockMenuPreset getPreset(@Nullable String id) { return id == null ? null : SlimefunPlugin.getRegistry().getMenuPresets().get(id); } diff --git a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java index 9cf7d9d4d..e6d31b065 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/DirtyChestMenu.java @@ -91,12 +91,14 @@ public class DirtyChestMenu extends ChestMenu { } public boolean fits(@Nonnull ItemStack item, int... slots) { - if (getItemInSlot(slots[0]) == null) { - // Very small optimization - return true; - } else { - return InvUtils.fits(toInventory(), new ItemStackWrapper(item), slots); + for (int slot : slots) { + // A small optimization for empty slots + if (getItemInSlot(slot) == null) { + return true; + } } + + return InvUtils.fits(toInventory(), new ItemStackWrapper(item), slots); } @Nullable diff --git a/src/main/java/me/mrCookieSlime/Slimefun/bstats/bukkit/Metrics.java b/src/main/java/me/mrCookieSlime/Slimefun/bstats/bukkit/Metrics.java deleted file mode 100644 index 5f44303db..000000000 --- a/src/main/java/me/mrCookieSlime/Slimefun/bstats/bukkit/Metrics.java +++ /dev/null @@ -1,741 +0,0 @@ -package me.mrCookieSlime.Slimefun.bstats.bukkit; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.RegisteredServiceProvider; -import org.bukkit.plugin.ServicePriority; - -import javax.net.ssl.HttpsURLConnection; -import java.io.*; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.Callable; -import java.util.logging.Level; -import java.util.zip.GZIPOutputStream; - -/** - * bStats collects some data for plugin authors. - *

- * Check out https://bStats.org/ to learn more about bStats! - * - * @deprecated This class is not guaranteed to be included in future versions of Slimefun! Please shade bStats yourself - * by following the instructions at: https://bstats.org/getting-started/include-metrics - */ -@Deprecated -public class Metrics { - - static { - // You can use the property to disable the check in your test environment - if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { - // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D - final String defaultPackage = new String(new byte[] { 'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't' }); - final String examplePackage = new String(new byte[] { 'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e' }); - // We want to make sure nobody just copy & pastes the example and use the wrong package names - if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) { - throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); - } - } - } - - // The version of this bStats class - public static final int B_STATS_VERSION = 1; - - // The url to which the data is sent - private static final String URL = "https://bStats.org/submitData/bukkit"; - - // Is bStats enabled on this server? - private boolean enabled; - - // Should failed requests be logged? - private static boolean logFailedRequests; - - // Should the sent data be logged? - private static boolean logSentData; - - // Should the response text be logged? - private static boolean logResponseStatusText; - - // The uuid of the server - private static String serverUUID; - - // The plugin - private final Plugin plugin; - - // The plugin id - private final int pluginId; - - // A list with all custom charts - private final List charts = new ArrayList<>(); - - /** - * Class constructor. - * - * @param plugin - * The plugin which stats should be submitted. - * @param pluginId - * The id of the plugin. - * It can be found at What is my plugin id? - */ - public Metrics(Plugin plugin, int pluginId) { - if (plugin == null) { - throw new IllegalArgumentException("Plugin cannot be null!"); - } - this.plugin = plugin; - this.pluginId = pluginId; - - // Get the config file - File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); - File configFile = new File(bStatsFolder, "config.yml"); - YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); - - // Check if the config file exists - if (!config.isSet("serverUuid")) { - - // Add default values - config.addDefault("enabled", true); - // Every server gets it's unique random id. - config.addDefault("serverUuid", UUID.randomUUID().toString()); - // Should failed request be logged? - config.addDefault("logFailedRequests", false); - // Should the sent data be logged? - config.addDefault("logSentData", false); - // Should the response text be logged? - config.addDefault("logResponseStatusText", false); - - // Inform the server owners about bStats - config.options().header("bStats collects some data for plugin authors like how many servers are using their plugins.\n" + "To honor their work, you should not disable it.\n" + "This has nearly no effect on the server performance!\n" + "Check out https://bStats.org/ to learn more :)").copyDefaults(true); - try { - config.save(configFile); - } catch (IOException ignored) {} - } - - // Load the data - enabled = config.getBoolean("enabled", true); - serverUUID = config.getString("serverUuid"); - logFailedRequests = config.getBoolean("logFailedRequests", false); - logSentData = config.getBoolean("logSentData", false); - logResponseStatusText = config.getBoolean("logResponseStatusText", false); - - if (enabled) { - boolean found = false; - // Search for all other bStats Metrics classes to see if we are the first one - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - found = true; // We aren't the first - break; - } catch (NoSuchFieldException ignored) {} - } - // Register our service - Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal); - if (!found) { - // We are the first! - startSubmitting(); - } - } - } - - /** - * Checks if bStats is enabled. - * - * @return Whether bStats is enabled or not. - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Adds a custom chart. - * - * @param chart - * The chart to add. - */ - public void addCustomChart(CustomChart chart) { - if (chart == null) { - throw new IllegalArgumentException("Chart cannot be null!"); - } - charts.add(chart); - } - - /** - * Starts the Scheduler which submits our data every 30 minutes. - */ - private void startSubmitting() { - final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags - timer.scheduleAtFixedRate(new TimerTask() { - - @Override - public void run() { - if (!plugin.isEnabled()) { // Plugin was disabled - timer.cancel(); - return; - } - // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit - // scheduler - // Don't be afraid! The connection to the bStats server is still async, only the stats collection is - // sync ;) - Bukkit.getScheduler().runTask(plugin, () -> submitData()); - } - }, 1000 * 60 * 5, 1000 * 60 * 30); - // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start - // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! - // WARNING: Just don't do it! - } - - /** - * Gets the plugin specific data. - * This method is called using Reflection. - * - * @return The plugin specific data. - */ - public JsonObject getPluginData() { - JsonObject data = new JsonObject(); - - String pluginName = plugin.getDescription().getName(); - String pluginVersion = plugin.getDescription().getVersion(); - - data.addProperty("pluginName", pluginName); // Append the name of the plugin - data.addProperty("id", pluginId); // Append the id of the plugin - data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin - JsonArray customCharts = new JsonArray(); - for (CustomChart customChart : charts) { - // Add the data of the custom charts - JsonObject chart = customChart.getRequestJsonObject(); - if (chart == null) { // If the chart is null, we skip it - continue; - } - customCharts.add(chart); - } - data.add("customCharts", customCharts); - - return data; - } - - /** - * Gets the server specific data. - * - * @return The server specific data. - */ - private JsonObject getServerData() { - // Minecraft specific data - int playerAmount; - try { - // Around MC 1.8 the return type was changed to a collection from an array, - // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; - Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); - playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; - } catch (Exception e) { - playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed - } - int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; - String bukkitVersion = Bukkit.getVersion(); - String bukkitName = Bukkit.getName(); - - // OS/Java specific data - String javaVersion = System.getProperty("java.version"); - String osName = System.getProperty("os.name"); - String osArch = System.getProperty("os.arch"); - String osVersion = System.getProperty("os.version"); - int coreCount = Runtime.getRuntime().availableProcessors(); - - JsonObject data = new JsonObject(); - - data.addProperty("serverUUID", serverUUID); - - data.addProperty("playerAmount", playerAmount); - data.addProperty("onlineMode", onlineMode); - data.addProperty("bukkitVersion", bukkitVersion); - data.addProperty("bukkitName", bukkitName); - - data.addProperty("javaVersion", javaVersion); - data.addProperty("osName", osName); - data.addProperty("osArch", osArch); - data.addProperty("osVersion", osVersion); - data.addProperty("coreCount", coreCount); - - return data; - } - - /** - * Collects the data and sends it afterwards. - */ - private void submitData() { - final JsonObject data = getServerData(); - - JsonArray pluginData = new JsonArray(); - // Search for all other bStats Metrics classes to get their plugin data - for (Class service : Bukkit.getServicesManager().getKnownServices()) { - try { - service.getField("B_STATS_VERSION"); // Our identifier :) - - for (RegisteredServiceProvider provider : Bukkit.getServicesManager().getRegistrations(service)) { - try { - Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider()); - if (plugin instanceof JsonObject) { - pluginData.add((JsonObject) plugin); - } else { // old bstats version compatibility - try { - Class jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject"); - if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) { - Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString"); - jsonStringGetter.setAccessible(true); - String jsonString = (String) jsonStringGetter.invoke(plugin); - JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject(); - pluginData.add(object); - } - } catch (ClassNotFoundException e) { - // minecraft version 1.14+ - if (logFailedRequests) { - this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception", e); - } - } - } - } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {} - } - } catch (NoSuchFieldException ignored) {} - } - - data.add("plugins", pluginData); - - // Create a new thread for the connection to the bStats server - new Thread(() -> { - try { - // Send the data - sendData(plugin, data); - } catch (Exception e) { - // Something went wrong! :( - if (logFailedRequests) { - plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); - } - } - }).start(); - } - - /** - * Sends the data to the bStats server. - * - * @param plugin - * Any plugin. It's just used to get a logger instance. - * @param data - * The data to send. - * @throws Exception - * If the request failed. - */ - private static void sendData(Plugin plugin, JsonObject data) throws Exception { - if (data == null) { - throw new IllegalArgumentException("Data cannot be null!"); - } - if (Bukkit.isPrimaryThread()) { - throw new IllegalAccessException("This method must not be called from the main thread!"); - } - if (logSentData) { - plugin.getLogger().info("Sending data to bStats: " + data); - } - HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); - - // Compress the data to save bandwidth - byte[] compressedData = compress(data.toString()); - - // Add headers - connection.setRequestMethod("POST"); - connection.addRequestProperty("Accept", "application/json"); - connection.addRequestProperty("Connection", "close"); - connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request - connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); - connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format - connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); - - // Send data - connection.setDoOutput(true); - try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { - outputStream.write(compressedData); - } - - StringBuilder builder = new StringBuilder(); - try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { - String line; - while ((line = bufferedReader.readLine()) != null) { - builder.append(line); - } - } - - if (logResponseStatusText) { - plugin.getLogger().info("Sent data to bStats and received response: " + builder); - } - } - - /** - * Gzips the given String. - * - * @param str - * The string to gzip. - * @return The gzipped String. - * @throws IOException - * If the compression failed. - */ - private static byte[] compress(final String str) throws IOException { - if (str == null) { - return null; - } - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { - gzip.write(str.getBytes(StandardCharsets.UTF_8)); - } - return outputStream.toByteArray(); - } - - /** - * Represents a custom chart. - */ - public static abstract class CustomChart { - - // The id of the chart - final String chartId; - - /** - * Class constructor. - * - * @param chartId - * The id of the chart. - */ - CustomChart(String chartId) { - if (chartId == null || chartId.isEmpty()) { - throw new IllegalArgumentException("ChartId cannot be null or empty!"); - } - this.chartId = chartId; - } - - private JsonObject getRequestJsonObject() { - JsonObject chart = new JsonObject(); - chart.addProperty("chartId", chartId); - try { - JsonObject data = getChartData(); - if (data == null) { - // If the data is null we don't send the chart. - return null; - } - chart.add("data", data); - } catch (Throwable t) { - if (logFailedRequests) { - Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); - } - return null; - } - return chart; - } - - protected abstract JsonObject getChartData() throws Exception; - - } - - /** - * Represents a custom simple pie. - */ - public static class SimplePie extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId - * The id of the chart. - * @param callable - * The callable which is used to request the chart data. - */ - public SimplePie(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - String value = callable.call(); - if (value == null || value.isEmpty()) { - // Null = skip the chart - return null; - } - data.addProperty("value", value); - return data; - } - } - - /** - * Represents a custom advanced pie. - */ - public static class AdvancedPie extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId - * The id of the chart. - * @param callable - * The callable which is used to request the chart data. - */ - public AdvancedPie(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - continue; // Skip this invalid - } - allSkipped = false; - values.addProperty(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.add("values", values); - return data; - } - } - - /** - * Represents a custom drilldown pie. - */ - public static class DrilldownPie extends CustomChart { - - private final Callable>> callable; - - /** - * Class constructor. - * - * @param chartId - * The id of the chart. - * @param callable - * The callable which is used to request the chart data. - */ - public DrilldownPie(String chartId, Callable>> callable) { - super(chartId); - this.callable = callable; - } - - @Override - public JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map> map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean reallyAllSkipped = true; - for (Map.Entry> entryValues : map.entrySet()) { - JsonObject value = new JsonObject(); - boolean allSkipped = true; - for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { - value.addProperty(valueEntry.getKey(), valueEntry.getValue()); - allSkipped = false; - } - if (!allSkipped) { - reallyAllSkipped = false; - values.add(entryValues.getKey(), value); - } - } - if (reallyAllSkipped) { - // Null = skip the chart - return null; - } - data.add("values", values); - return data; - } - } - - /** - * Represents a custom single line chart. - */ - public static class SingleLineChart extends CustomChart { - - private final Callable callable; - - /** - * Class constructor. - * - * @param chartId - * The id of the chart. - * @param callable - * The callable which is used to request the chart data. - */ - public SingleLineChart(String chartId, Callable callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - int value = callable.call(); - if (value == 0) { - // Null = skip the chart - return null; - } - data.addProperty("value", value); - return data; - } - - } - - /** - * Represents a custom multi line chart. - */ - public static class MultiLineChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId - * The id of the chart. - * @param callable - * The callable which is used to request the chart data. - */ - public MultiLineChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() == 0) { - continue; // Skip this invalid - } - allSkipped = false; - values.addProperty(entry.getKey(), entry.getValue()); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.add("values", values); - return data; - } - - } - - /** - * Represents a custom simple bar chart. - */ - public static class SimpleBarChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId - * The id of the chart. - * @param callable - * The callable which is used to request the chart data. - */ - public SimpleBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - for (Map.Entry entry : map.entrySet()) { - JsonArray categoryValues = new JsonArray(); - categoryValues.add(new JsonPrimitive(entry.getValue())); - values.add(entry.getKey(), categoryValues); - } - data.add("values", values); - return data; - } - - } - - /** - * Represents a custom advanced bar chart. - */ - public static class AdvancedBarChart extends CustomChart { - - private final Callable> callable; - - /** - * Class constructor. - * - * @param chartId - * The id of the chart. - * @param callable - * The callable which is used to request the chart data. - */ - public AdvancedBarChart(String chartId, Callable> callable) { - super(chartId); - this.callable = callable; - } - - @Override - protected JsonObject getChartData() throws Exception { - JsonObject data = new JsonObject(); - JsonObject values = new JsonObject(); - Map map = callable.call(); - if (map == null || map.isEmpty()) { - // Null = skip the chart - return null; - } - boolean allSkipped = true; - for (Map.Entry entry : map.entrySet()) { - if (entry.getValue().length == 0) { - continue; // Skip this invalid - } - allSkipped = false; - JsonArray categoryValues = new JsonArray(); - for (int categoryValue : entry.getValue()) { - categoryValues.add(new JsonPrimitive(categoryValue)); - } - values.add(entry.getKey(), categoryValues); - } - if (allSkipped) { - // Null = skip the chart - return null; - } - data.add("values", values); - return data; - } - } - -} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 41c82a88a..9413104e2 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -10,7 +10,6 @@ options: armor-update-interval: 10 enable-armor-effects: true auto-save-delay-in-minutes: 10 - emerald-enchantment-limit: 2 legacy-ore-washer: false legacy-dust-washer: false legacy-ore-grinder: true @@ -38,6 +37,7 @@ networks: max-size: 200 cargo-ticker-delay: 0 enable-visualizer: true + delete-excess-items: false items: talismans: true diff --git a/src/main/resources/languages/categories_pt.yml b/src/main/resources/languages/categories_pt.yml new file mode 100644 index 000000000..d208b9c0c --- /dev/null +++ b/src/main/resources/languages/categories_pt.yml @@ -0,0 +1,26 @@ +--- +slimefun: + weapons: Armas + tools: Ferramentas + items: Itens úteis + food: Comida + basic_machines: Máquinas básicas + electricity: Energia e Eletricidade + gps: Máquinas baseadas em GPS + armor: Armaduras + magical_items: Itens mágicos + magical_gadgets: Acessórios Mágicos + misc: Itens Variados + technical_gadgets: Dispositivos Técnicos + resources: Recursos + cargo: Gestão de Carga + tech_misc: Componentes Técnicos + magical_armor: Armaduras Mágicas + talismans: Talismãs (Tier I) + ender_talismans: Ender Talismãs (Tier II) + christmas: Natal (Dezembro) + valentines_day: Dia dos Namorados (14 de Fevereiro) + easter: Páscoa (Abril) + birthday: Aniversário do TheBusyBiscuit (26 de Outubro) + halloween: Dia das Bruxas (31 de Outubro) + androids: Androids Programáveis diff --git a/src/main/resources/languages/researches_pt-BR.yml b/src/main/resources/languages/researches_pt-BR.yml index 1333f704b..f4ef860fc 100644 --- a/src/main/resources/languages/researches_pt-BR.yml +++ b/src/main/resources/languages/researches_pt-BR.yml @@ -236,3 +236,14 @@ slimefun: advanced_industrial_miner: Melhor mineração magical_zombie_pills: Deszombificação auto_brewer: Cervejaria Industrial + enchantment_rune: Encantador Antigo + lead_clothing: Roupa de Chumbo + tape_measure: Fita métrica + iron_golem_assembler: Golems de Ferro Automatizados + climbing_pick: Bloco atacante + shulker_shell: Shulkers Sintética + villager_rune: Resetador de trocas dos aldeões + caveman_talisman: Talismã do Homem das Cavernas + even_higher_tier_capacitors: Capacitores tier 3 + elytra_cap: Engrenagem de colisão + energy_connectors: Fios de conexão diff --git a/src/main/resources/languages/researches_zh-CN.yml b/src/main/resources/languages/researches_zh-CN.yml index 3b2187c3a..a8542078a 100644 --- a/src/main/resources/languages/researches_zh-CN.yml +++ b/src/main/resources/languages/researches_zh-CN.yml @@ -245,4 +245,5 @@ slimefun: villager_rune: 防奸商神器 caveman_talisman: 穴居人护身符 even_higher_tier_capacitors: 三级电容器 + elytra_cap: 无伤落地 energy_connectors: 有线连接 diff --git a/src/main/resources/tags/spawn_eggs.json b/src/main/resources/tags/spawn_eggs.json new file mode 100644 index 000000000..3087fa5d4 --- /dev/null +++ b/src/main/resources/tags/spawn_eggs.json @@ -0,0 +1,89 @@ +{ + "values" : [ + "minecraft:bat_spawn_egg", + { + "id" : "minecraft:bee_spawn_egg", + "required" : false + }, + "minecraft:blaze_spawn_egg", + "minecraft:cave_spider_spawn_egg", + "minecraft:cat_spawn_egg", + "minecraft:chicken_spawn_egg", + "minecraft:cod_spawn_egg", + "minecraft:cow_spawn_egg", + "minecraft:creeper_spawn_egg", + "minecraft:dolphin_spawn_egg", + "minecraft:donkey_spawn_egg", + "minecraft:drowned_spawn_egg", + "minecraft:elder_guardian_spawn_egg", + "minecraft:enderman_spawn_egg", + "minecraft:endermite_spawn_egg", + "minecraft:evoker_spawn_egg", + "minecraft:fox_spawn_egg", + "minecraft:ghast_spawn_egg", + "minecraft:guardian_spawn_egg", + { + "id" : "minecraft:hoglin_spawn_egg", + "required" : false + }, + "minecraft:horse_spawn_egg", + "minecraft:husk_spawn_egg", + "minecraft:llama_spawn_egg", + "minecraft:magma_cube_spawn_egg", + "minecraft:mooshroom_spawn_egg", + "minecraft:mule_spawn_egg", + "minecraft:ocelot_spawn_egg", + "minecraft:panda_spawn_egg", + "minecraft:parrot_spawn_egg", + "minecraft:phantom_spawn_egg", + "minecraft:pig_spawn_egg", + { + "id" : "minecraft:piglin_spawn_egg", + "required" : false + }, + { + "id" : "minecraft:piglin_brute_spawn_egg", + "required" : false + }, + "minecraft:pillager_spawn_egg", + "minecraft:polar_bear_spawn_egg", + "minecraft:pufferfish_spawn_egg", + "minecraft:rabbit_spawn_egg", + "minecraft:ravager_spawn_egg", + "minecraft:salmon_spawn_egg", + "minecraft:sheep_spawn_egg", + "minecraft:shulker_spawn_egg", + "minecraft:silverfish_spawn_egg", + "minecraft:skeleton_spawn_egg", + "minecraft:skeleton_horse_spawn_egg", + "minecraft:slime_spawn_egg", + "minecraft:spider_spawn_egg", + "minecraft:squid_spawn_egg", + "minecraft:stray_spawn_egg", + { + "id" : "minecraft:strider_spawn_egg", + "required" : false + }, + "minecraft:trader_llama_spawn_egg", + "minecraft:tropical_fish_spawn_egg", + "minecraft:turtle_spawn_egg", + "minecraft:vex_spawn_egg", + "minecraft:villager_spawn_egg", + "minecraft:vindicator_spawn_egg", + "minecraft:wandering_trader_spawn_egg", + "minecraft:witch_spawn_egg", + "minecraft:wither_skeleton_spawn_egg", + "minecraft:wolf_spawn_egg", + { + "id" : "minecraft:zoglin_spawn_egg", + "required" : false + }, + "minecraft:zombie_spawn_egg", + "minecraft:zombie_horse_spawn_egg", + "minecraft:zombie_villager_spawn_egg", + { + "id" : "minecraft:zombified_piglin_spawn_egg", + "required" : false + } + ] +} diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/interfaces/SlimefunItemTest.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/interfaces/SlimefunItemTest.java index d97f56f91..55835ebb1 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/interfaces/SlimefunItemTest.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/interfaces/SlimefunItemTest.java @@ -17,6 +17,15 @@ import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; +/** + * This is a convenient interface for us to use in unit test classes + * that test the functionality of a particular {@link SlimefunItem}. + * + * @author TheBusyBiscuit + * + * @param + * The class type of {@link SlimefunItem} you want to test + */ @FunctionalInterface public interface SlimefunItemTest { diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/TestSlimefunItem.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/TestSlimefunItem.java new file mode 100644 index 000000000..343303f1a --- /dev/null +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/items/TestSlimefunItem.java @@ -0,0 +1,179 @@ +package io.github.thebusybiscuit.slimefun4.testing.tests.items; + +import java.util.Optional; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import be.seeseemelk.mockbukkit.MockBukkit; +import io.github.thebusybiscuit.cscorelib2.item.CustomItem; +import io.github.thebusybiscuit.slimefun4.api.exceptions.UnregisteredItemException; +import io.github.thebusybiscuit.slimefun4.api.exceptions.WrongItemStackException; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +import io.github.thebusybiscuit.slimefun4.testing.TestUtilities; +import me.mrCookieSlime.Slimefun.Lists.RecipeType; +import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; +import me.mrCookieSlime.Slimefun.api.SlimefunItemStack; + +class TestSlimefunItem { + + private static SlimefunPlugin plugin; + + @BeforeAll + public static void load() { + MockBukkit.mock(); + plugin = MockBukkit.load(SlimefunPlugin.class); + } + + @AfterAll + public static void unload() { + MockBukkit.unmock(); + } + + @Test + @DisplayName("Test wiki pages getting assigned correctly") + void testWikiPages() { + SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "WIKI_ITEM", new CustomItem(Material.BOOK, "&cTest")); + item.register(plugin); + + Assertions.assertFalse(item.getWikipage().isPresent()); + + // null should not be a valid argument + Assertions.assertThrows(IllegalArgumentException.class, () -> item.addOficialWikipage(null)); + + item.addOficialWikipage("Test"); + + Optional wiki = item.getWikipage(); + Assertions.assertTrue(wiki.isPresent()); + Assertions.assertEquals("https://github.com/Slimefun/Slimefun4/wiki/Test", wiki.get()); + } + + @Test + @DisplayName("Test SlimefunItem registering Recipes properly") + void testRecipe() { + SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RECIPE_TEST", new CustomItem(Material.DIAMOND, "&dAnother one bites the test")); + + ItemStack[] recipe = { null, new ItemStack(Material.DIAMOND), null, null, new ItemStack(Material.DIAMOND), null, null, new ItemStack(Material.DIAMOND), null }; + item.setRecipe(recipe); + item.register(plugin); + + Assertions.assertArrayEquals(recipe, item.getRecipe()); + + Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipe(null)); + Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipe(new ItemStack[3])); + Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipe(new ItemStack[20])); + } + + @Test + @DisplayName("Test Recipe outputs being handled correctly") + void testRecipeOutput() { + SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RECIPE_OUTPUT_TEST", new CustomItem(Material.DIAMOND, "&cTest")); + item.register(plugin); + + Assertions.assertEquals(item.getItem(), item.getRecipeOutput()); + + ItemStack output = new ItemStack(Material.EMERALD, 64); + item.setRecipeOutput(output); + Assertions.assertEquals(output, item.getRecipeOutput()); + + item.setRecipeOutput(item.getItem()); + Assertions.assertEquals(item.getItem(), item.getRecipeOutput()); + } + + @Test + @DisplayName("Test Recipe Types being handled properly") + void testRecipeType() { + SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RECIPE_TYPE_TEST", new CustomItem(Material.DIAMOND, "&cTest")); + item.register(plugin); + + Assertions.assertNotNull(item.getRecipeType()); + + item.setRecipeType(RecipeType.ENHANCED_CRAFTING_TABLE); + Assertions.assertEquals(RecipeType.ENHANCED_CRAFTING_TABLE, item.getRecipeType()); + + item.setRecipeType(RecipeType.NULL); + Assertions.assertEquals(RecipeType.NULL, item.getRecipeType()); + + Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipeType(null)); + } + + @ParameterizedTest + @DisplayName("Test SlimefunItem#isItem(...)") + @ValueSource(booleans = { true, false }) + void testIsItem(boolean compatibility) { + CustomItem item = new CustomItem(Material.BEACON, "&cItem Test"); + String id = "IS_ITEM_TEST" + (compatibility ? "_COMPATIBLE" : ""); + SlimefunItem sfItem = TestUtilities.mockSlimefunItem(plugin, id, item); + sfItem.register(plugin); + + Assertions.assertTrue(sfItem.isItem(sfItem.getItem())); + + Assertions.assertFalse(sfItem.isItem(null)); + Assertions.assertFalse(sfItem.isItem(new ItemStack(Material.BEACON))); + Assertions.assertFalse(sfItem.isItem(new CustomItem(Material.REDSTONE, "&cTest"))); + + if (compatibility) { + SlimefunPlugin.getRegistry().setBackwardsCompatible(true); + + Assertions.assertEquals(sfItem, SlimefunItem.getByItem(item)); + Assertions.assertTrue(sfItem.isItem(item)); + Assertions.assertTrue(sfItem.isItem(new CustomItem(Material.BEACON, "&cItem Test"))); + + SlimefunPlugin.getRegistry().setBackwardsCompatible(false); + } else { + Assertions.assertFalse(sfItem.isItem(item)); + Assertions.assertFalse(sfItem.isItem(new CustomItem(Material.BEACON, "&cItem Test"))); + } + + Assertions.assertEquals(sfItem, SlimefunItem.getByItem(new SlimefunItemStack(sfItem.getId(), item))); + } + + @Test + @DisplayName("Test WrongItemStackException") + void testWrongItemStackException() { + SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "WRONG_ITEMSTACK_EXCEPTION", new CustomItem(Material.NETHER_STAR, "&4Do not modify me")); + item.register(plugin); + item.load(); + + ItemStack itemStack = item.getItem(); + Assertions.assertThrows(WrongItemStackException.class, () -> itemStack.setAmount(40)); + } + + @Test + @DisplayName("Test UnregisteredItemException") + void testUnregisteredItemException() { + SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "UNREGISTERED_ITEM_EXCEPTION", new CustomItem(Material.NETHER_STAR, "&4Do not modify me")); + Assertions.assertThrows(UnregisteredItemException.class, () -> item.getAddon()); + } + + @Test + @DisplayName("Test SlimefunItem#equals(...)") + void testEquals() { + SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "EQUALS_TEST", new CustomItem(Material.LANTERN, "&6We are equal")); + SlimefunItem item2 = TestUtilities.mockSlimefunItem(plugin, "EQUALS_TEST", new CustomItem(Material.LANTERN, "&6We are equal")); + SlimefunItem differentItem = TestUtilities.mockSlimefunItem(plugin, "I_AM_DIFFERENT", new CustomItem(Material.LANTERN, "&6We are equal")); + + Assertions.assertEquals(item, item2); + Assertions.assertNotEquals(item, differentItem); + Assertions.assertNotEquals(item2, differentItem); + } + + @Test + @DisplayName("Test SlimefunItem#hashCode()") + void testHashCode() { + SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "EQUALS_TEST", new CustomItem(Material.LANTERN, "&6We are equal")); + SlimefunItem item2 = TestUtilities.mockSlimefunItem(plugin, "EQUALS_TEST", new CustomItem(Material.LANTERN, "&6We are equal")); + SlimefunItem differentItem = TestUtilities.mockSlimefunItem(plugin, "I_AM_DIFFERENT", new CustomItem(Material.LANTERN, "&6We are equal")); + + Assertions.assertEquals(item.hashCode(), item2.hashCode()); + Assertions.assertNotEquals(item.hashCode(), differentItem.hashCode()); + Assertions.assertNotEquals(item2.hashCode(), differentItem.hashCode()); + } +} diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestCauldronListener.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestCauldronListener.java index dbe1ad358..75daede45 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestCauldronListener.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestCauldronListener.java @@ -86,7 +86,7 @@ class TestCauldronListener { @Test @DisplayName("Test Cauldron being cancelled with slimefun leather armor") void testCauldronWithSlimefunLeatherArmor() { - SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CAULDRON_TEST_MOCK_LEATHER", new CustomItem(Material.LEATHER_BOOTS, "&6Mock")); + SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CAULDRON_TEST_MOCK_LEATHER", new CustomItem(Material.LEATHER_BOOTS, "&6Mock Leather Armor")); item.register(plugin); PlayerInteractEvent event = mockCauldronEvent(item.getItem()); diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestNetworkListener.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestNetworkListener.java index 4e8e1b24e..9506c80af 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestNetworkListener.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/listeners/TestNetworkListener.java @@ -25,7 +25,7 @@ class TestNetworkListener { private static SlimefunPlugin plugin; private static NetworkListener listener; - private static NetworkManager manager = new NetworkManager(80, false); + private static NetworkManager manager = new NetworkManager(80); private static ServerMock server; @BeforeAll diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/networks/TestNetworkManager.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/networks/TestNetworkManager.java index 4aec2a175..161bffdc3 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/networks/TestNetworkManager.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/networks/TestNetworkManager.java @@ -11,6 +11,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mockito; import be.seeseemelk.mockbukkit.MockBukkit; @@ -37,23 +39,41 @@ class TestNetworkManager { @Test @DisplayName("Test illegal network size arguments") void testIllegalNetworkSize() { - Assertions.assertThrows(IllegalArgumentException.class, () -> new NetworkManager(-100, false)); - Assertions.assertThrows(IllegalArgumentException.class, () -> new NetworkManager(0, false)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new NetworkManager(-100)); + Assertions.assertThrows(IllegalArgumentException.class, () -> new NetworkManager(0)); } @Test @DisplayName("Test maximum network size") void testGetMaxNetworkSize() { int size = 50; - NetworkManager manager = new NetworkManager(size, false); + NetworkManager manager = new NetworkManager(size); Assertions.assertEquals(size, manager.getMaxSize()); } + @ParameterizedTest + @ValueSource(booleans = { true, false }) + @DisplayName("Test visualizer setting") + void testVisualizerSetting(boolean enabled) { + NetworkManager manager = new NetworkManager(200, enabled, false); + + Assertions.assertEquals(enabled, manager.isVisualizerEnabled()); + } + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + @DisplayName("Test item deletion setting") + void testItemDeletionSetting(boolean enabled) { + NetworkManager manager = new NetworkManager(200, true, enabled); + + Assertions.assertEquals(enabled, manager.isItemDeletionEnabled()); + } + @Test @DisplayName("Test network list") void testGetNetworkList() { - NetworkManager manager = new NetworkManager(10, false); + NetworkManager manager = new NetworkManager(10); World world = server.addSimpleWorld("Simple Network World"); Location loc = new Location(world, 0, 100, 0); @@ -82,7 +102,7 @@ class TestNetworkManager { @Test @DisplayName("Test getting a network at a location") void testGetNetworkAtLocation() { - NetworkManager manager = new NetworkManager(10, false); + NetworkManager manager = new NetworkManager(10); World world = server.addSimpleWorld("Simple Network World"); Location loc = new Location(world, 0, 100, 0); Location loc2 = new Location(world, 0, 200, 0); @@ -104,7 +124,7 @@ class TestNetworkManager { @Test @DisplayName("Test getting all networks at a location") void testGetNetworksAtLocation() { - NetworkManager manager = new NetworkManager(10, false); + NetworkManager manager = new NetworkManager(10); World world = server.addSimpleWorld("Simple Network World"); Location loc = new Location(world, 0, 100, 0); Location loc2 = new Location(world, 0, 200, 0); @@ -120,7 +140,7 @@ class TestNetworkManager { @Test @DisplayName("Test a single node network") void testSingleNodeNetwork() { - NetworkManager manager = new NetworkManager(1, false); + NetworkManager manager = new NetworkManager(1); World world = server.addSimpleWorld("Simple Network World"); Location loc = new Location(world, 0, 100, 0); @@ -134,7 +154,7 @@ class TestNetworkManager { @Test @DisplayName("Test networks connecting via corners") void testCornerConnection() { - NetworkManager manager = new NetworkManager(100, false); + NetworkManager manager = new NetworkManager(100); World world = server.addSimpleWorld("Simple Network World"); Map map = new HashMap<>(); diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/registration/TestSlimefunItemRegistration.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/registration/TestSlimefunItemRegistration.java index 3d67f32e5..aa47fa5b4 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/registration/TestSlimefunItemRegistration.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/registration/TestSlimefunItemRegistration.java @@ -1,16 +1,12 @@ package io.github.thebusybiscuit.slimefun4.testing.tests.registration; -import java.util.Optional; - import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.inventory.ItemStack; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; import be.seeseemelk.mockbukkit.MockBukkit; import io.github.thebusybiscuit.cscorelib2.item.CustomItem; @@ -19,12 +15,10 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemState; import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem; import io.github.thebusybiscuit.slimefun4.testing.TestUtilities; -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; -public class TestSlimefunItemRegistration { +class TestSlimefunItemRegistration { private static SlimefunPlugin plugin; @@ -40,7 +34,8 @@ public class TestSlimefunItemRegistration { } @Test - public void testSuccessfulRegistration() { + @DisplayName("Test SlimefunItem registering properly") + void testSuccessfulRegistration() { String id = "TEST_ITEM"; SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, id, new CustomItem(Material.DIAMOND, "&cTest")); @@ -55,7 +50,8 @@ public class TestSlimefunItemRegistration { } @Test - public void testDisabledItem() { + @DisplayName("Test disabled SlimefunItem being disabled") + void testDisabledItem() { SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "DISABLED_ITEM", new CustomItem(Material.DIAMOND, "&cTest")); SlimefunPlugin.getItemCfg().setValue("DISABLED_ITEM.enabled", false); item.register(plugin); @@ -65,24 +61,8 @@ public class TestSlimefunItemRegistration { } @Test - public void testWikiPages() { - SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "WIKI_ITEM", new CustomItem(Material.BOOK, "&cTest")); - item.register(plugin); - - Assertions.assertFalse(item.getWikipage().isPresent()); - - // null should not be a valid argument - Assertions.assertThrows(IllegalArgumentException.class, () -> item.addOficialWikipage(null)); - - item.addOficialWikipage("Test"); - - Optional wiki = item.getWikipage(); - Assertions.assertTrue(wiki.isPresent()); - Assertions.assertEquals("https://github.com/Slimefun/Slimefun4/wiki/Test", wiki.get()); - } - - @Test - public void testVanillaItemFallback() { + @DisplayName("Test VanillaItem falling back to vanilla.") + void testVanillaItemFallback() { VanillaItem item = TestUtilities.mockVanillaItem(plugin, Material.ACACIA_SIGN, false); item.register(plugin); @@ -92,7 +72,8 @@ public class TestSlimefunItemRegistration { } @Test - public void testIdConflict() { + @DisplayName("Test id conflicts being handled with an exception") + void testIdConflict() { SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "DUPLICATE_ID", new CustomItem(Material.DIAMOND, "&cTest")); item.register(plugin); @@ -104,83 +85,8 @@ public class TestSlimefunItemRegistration { } @Test - public void testRecipe() { - SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RECIPE_TEST", new CustomItem(Material.DIAMOND, "&dAnother one bites the test")); - - ItemStack[] recipe = { null, new ItemStack(Material.DIAMOND), null, null, new ItemStack(Material.DIAMOND), null, null, new ItemStack(Material.DIAMOND), null }; - item.setRecipe(recipe); - item.register(plugin); - - Assertions.assertArrayEquals(recipe, item.getRecipe()); - - Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipe(null)); - Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipe(new ItemStack[3])); - Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipe(new ItemStack[20])); - } - - @Test - public void testRecipeOutput() { - SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RECIPE_OUTPUT_TEST", new CustomItem(Material.DIAMOND, "&cTest")); - item.register(plugin); - - Assertions.assertEquals(item.getItem(), item.getRecipeOutput()); - - ItemStack output = new ItemStack(Material.EMERALD, 64); - item.setRecipeOutput(output); - Assertions.assertEquals(output, item.getRecipeOutput()); - - item.setRecipeOutput(item.getItem()); - Assertions.assertEquals(item.getItem(), item.getRecipeOutput()); - } - - @Test - public void testRecipeType() { - SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RECIPE_TYPE_TEST", new CustomItem(Material.DIAMOND, "&cTest")); - item.register(plugin); - - Assertions.assertNotNull(item.getRecipeType()); - - item.setRecipeType(RecipeType.ENHANCED_CRAFTING_TABLE); - Assertions.assertEquals(RecipeType.ENHANCED_CRAFTING_TABLE, item.getRecipeType()); - - item.setRecipeType(RecipeType.NULL); - Assertions.assertEquals(RecipeType.NULL, item.getRecipeType()); - - Assertions.assertThrows(IllegalArgumentException.class, () -> item.setRecipeType(null)); - } - - @ParameterizedTest - @ValueSource(booleans = { true, false }) - public void testIsItem(boolean compatibility) { - CustomItem item = new CustomItem(Material.BEACON, "&cItem Test"); - String id = "IS_ITEM_TEST" + (compatibility ? "_COMPATIBLE" : ""); - SlimefunItem sfItem = TestUtilities.mockSlimefunItem(plugin, id, item); - sfItem.register(plugin); - - Assertions.assertTrue(sfItem.isItem(sfItem.getItem())); - - Assertions.assertFalse(sfItem.isItem(null)); - Assertions.assertFalse(sfItem.isItem(new ItemStack(Material.BEACON))); - Assertions.assertFalse(sfItem.isItem(new CustomItem(Material.REDSTONE, "&cTest"))); - - if (compatibility) { - SlimefunPlugin.getRegistry().setBackwardsCompatible(true); - - Assertions.assertEquals(sfItem, SlimefunItem.getByItem(item)); - Assertions.assertTrue(sfItem.isItem(item)); - Assertions.assertTrue(sfItem.isItem(new CustomItem(Material.BEACON, "&cItem Test"))); - - SlimefunPlugin.getRegistry().setBackwardsCompatible(false); - } else { - Assertions.assertFalse(sfItem.isItem(item)); - Assertions.assertFalse(sfItem.isItem(new CustomItem(Material.BEACON, "&cItem Test"))); - } - - Assertions.assertEquals(sfItem, SlimefunItem.getByItem(new SlimefunItemStack(sfItem.getId(), item))); - } - - @Test - public void testCategoryRegistration() { + @DisplayName("Test Category registration when registering an item") + void testCategoryRegistration() { SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CATEGORY_TEST", new CustomItem(Material.DIAMOND, "&cTest")); item.register(plugin); item.load(); @@ -202,7 +108,8 @@ public class TestSlimefunItemRegistration { } @Test - public void testHiddenItem() { + @DisplayName("Test hidden items being hidden") + void testHiddenItem() { SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "HIDDEN_TEST", new CustomItem(Material.DIAMOND, "&cTest")); item.setHidden(true); item.register(plugin); diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/services/TestRecipeService.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/services/TestRecipeService.java index 56ff6c85a..b2e60d55f 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/services/TestRecipeService.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/services/TestRecipeService.java @@ -96,6 +96,9 @@ class TestRecipeService { Optional optional2 = service.getFurnaceOutput(new ItemStack(Material.COBBLESTONE)); Assertions.assertTrue(optional2.isPresent()); Assertions.assertEquals(result, optional2.get()); + + // Cobblestone should be smeltable + Assertions.assertTrue(service.isSmeltable(new ItemStack(Material.COBBLESTONE))); } @Test diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/tasks/TestArmorTask.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/tasks/TestArmorTask.java index 6985c90c5..cb40849bc 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/tasks/TestArmorTask.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/tasks/TestArmorTask.java @@ -2,6 +2,7 @@ package io.github.thebusybiscuit.slimefun4.testing.tests.tasks; import java.util.Arrays; import java.util.Locale; +import java.util.stream.Stream; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -14,7 +15,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import be.seeseemelk.mockbukkit.MockBukkit; import be.seeseemelk.mockbukkit.ServerMock; @@ -32,13 +34,11 @@ class TestArmorTask { private static ServerMock server; private static SlimefunPlugin plugin; - private static ArmorTask task; @BeforeAll public static void load() { server = MockBukkit.mock(); plugin = MockBukkit.load(SlimefunPlugin.class); - task = new ArmorTask(false); } @AfterAll @@ -63,7 +63,7 @@ class TestArmorTask { player.getInventory().setHelmet(helmet.clone()); player.getInventory().setChestplate(new ItemStack(Material.DIAMOND_CHESTPLATE)); - task.run(); + new ArmorTask(false).run(); // Check if all Potion Effects were applied Assertions.assertTrue(player.getActivePotionEffects().containsAll(Arrays.asList(effects))); @@ -71,8 +71,8 @@ class TestArmorTask { @ParameterizedTest @DisplayName("Test Radiation and Hazmat Suits") - @ValueSource(booleans = { true, false }) - void testRadiactivity(boolean hazmat) throws InterruptedException { + @MethodSource("cartesianBooleans") + void testRadiactivity(boolean hazmat, boolean radioactiveFire) throws InterruptedException { Player player = server.addPlayer(); TestUtilities.awaitProfile(player); @@ -80,25 +80,38 @@ class TestArmorTask { player.getWorld().setTime(16000); Category category = TestUtilities.getCategory(plugin, "hazmat_suit_test"); - SlimefunItemStack item = new SlimefunItemStack("MOCK_URANIUM_" + String.valueOf(hazmat).toUpperCase(Locale.ROOT), Material.EMERALD, "&aHi, I am deadly"); + SlimefunItemStack item = new SlimefunItemStack("MOCK_URANIUM_" + String.valueOf(hazmat).toUpperCase(Locale.ROOT) + "_" + String.valueOf(radioactiveFire).toUpperCase(Locale.ROOT), Material.EMERALD, "&aHi, I am deadly"); new RadioactiveItem(category, Radioactivity.VERY_DEADLY, item, RecipeType.NULL, new ItemStack[9]).register(plugin); player.getInventory().setItemInMainHand(item.clone()); player.getInventory().setItemInOffHand(new ItemStack(Material.EMERALD_ORE)); if (hazmat) { - SlimefunItemStack chestplate = new SlimefunItemStack("MOCK_HAZMAT_SUIT", Material.LEATHER_CHESTPLATE, "&4Hazmat Prototype"); + SlimefunItemStack chestplate = new SlimefunItemStack("MOCK_HAZMAT_SUIT_" + String.valueOf(radioactiveFire).toUpperCase(Locale.ROOT), Material.LEATHER_CHESTPLATE, "&4Hazmat Prototype"); MockHazmatSuit armor = new MockHazmatSuit(category, chestplate); armor.register(plugin); player.getInventory().setChestplate(chestplate.clone()); } + ArmorTask task = new ArmorTask(radioactiveFire); task.run(); // Check if the Player is suffering from radiation boolean radiation = player.getActivePotionEffects().containsAll(task.getRadiationEffects()); Assertions.assertEquals(!hazmat, radiation); + Assertions.assertEquals(!hazmat && radioactiveFire, player.getFireTicks() > 0); + } + + /** + * This returns an {@link Arguments} {@link Stream} of boolean combinations. + * It performs a cartesian product on two boolean sets. + * + * @return a {@link Stream} of {@link Arguments} + */ + private static Stream cartesianBooleans() { + Stream stream = Stream.of(true, false); + return stream.flatMap(a -> Stream.of(true, false).map(b -> Arguments.of(a, b))); } } diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestLoreComparison.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestLoreComparison.java new file mode 100644 index 000000000..e25450b7b --- /dev/null +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestLoreComparison.java @@ -0,0 +1,112 @@ +package io.github.thebusybiscuit.slimefun4.testing.tests.utils; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.ChatColor; +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 be.seeseemelk.mockbukkit.MockBukkit; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin; +import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; + +class TestLoreComparison { + + private static final String IGNORED_LINE = ChatColor.GRAY + "Soulbound"; + + @BeforeAll + public static void load() { + MockBukkit.mock(); + MockBukkit.load(SlimefunPlugin.class); + } + + @AfterAll + public static void unload() { + MockBukkit.unmock(); + } + + @Test + @DisplayName("Test if two equal lores are considered equal") + void testEqualLists() { + List a = Arrays.asList("a", "b", "c"); + List b = Arrays.asList("a", "b", "c"); + + Assertions.assertTrue(SlimefunUtils.equalsLore(a, b)); + Assertions.assertTrue(SlimefunUtils.equalsLore(b, a)); + } + + @Test + @DisplayName("Test if two not equal lores are not considered equal") + void testNotEqualLists() { + List a = Arrays.asList("a", "b", "c"); + List b = Arrays.asList("1", "2", "3"); + + Assertions.assertFalse(SlimefunUtils.equalsLore(a, b)); + Assertions.assertFalse(SlimefunUtils.equalsLore(b, a)); + } + + @Test + @DisplayName("Test if lores with different length are not considered equal") + void testDifferentLengthLists() { + List a = Arrays.asList("a", "b", "c"); + List b = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); + + Assertions.assertFalse(SlimefunUtils.equalsLore(a, b)); + Assertions.assertFalse(SlimefunUtils.equalsLore(b, a)); + } + + @Test + @DisplayName("Test if lores with one ignored element are considered equal") + void testIgnoredElementLists() { + List a = Arrays.asList("a", "b", IGNORED_LINE, "c"); + List b = Arrays.asList("a", "b", "c"); + + Assertions.assertTrue(SlimefunUtils.equalsLore(a, b)); + Assertions.assertTrue(SlimefunUtils.equalsLore(b, a)); + } + + @Test + @DisplayName("Test if lores with ignored elements are considered equal") + void testIgnoredElementsLists() { + List a = Arrays.asList("a", "b", IGNORED_LINE, "c"); + List b = Arrays.asList("a", IGNORED_LINE, "b", "c"); + + Assertions.assertTrue(SlimefunUtils.equalsLore(a, b)); + Assertions.assertTrue(SlimefunUtils.equalsLore(b, a)); + } + + @Test + @DisplayName("Test if lores with ignored element at start are considered equal") + void testIgnoredElementStartLists() { + List a = Arrays.asList(IGNORED_LINE, "a", "b", "c"); + List b = Arrays.asList(IGNORED_LINE, "a", "b", "c"); + + Assertions.assertTrue(SlimefunUtils.equalsLore(a, b)); + Assertions.assertTrue(SlimefunUtils.equalsLore(b, a)); + } + + @Test + @DisplayName("Test if lores with ignored element at end are considered equal") + void testIgnoredElementEndLists() { + List a = Arrays.asList("a", "b", "c", IGNORED_LINE); + List b = Arrays.asList("a", "b", "c", IGNORED_LINE); + + Assertions.assertTrue(SlimefunUtils.equalsLore(a, b)); + Assertions.assertTrue(SlimefunUtils.equalsLore(b, a)); + } + + @Test + @DisplayName("Test if different lores with ignored element are not considered equal") + void testIgnoredElementsNotEqualLists() { + List a = Arrays.asList("a", "b", "c", "d"); + List b = Arrays.asList(IGNORED_LINE, "a", "b", "c", IGNORED_LINE); + + Assertions.assertFalse(SlimefunUtils.equalsLore(a, b)); + Assertions.assertFalse(SlimefunUtils.equalsLore(b, a)); + } + +} diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestNumberUtils.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestNumberUtils.java index 6b33de558..a6c61ea94 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestNumberUtils.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestNumberUtils.java @@ -26,19 +26,19 @@ class TestNumberUtils { @Test @DisplayName("Test elapsed time string") void testElapsedTime() { - LocalDateTime start = LocalDateTime.now(); + LocalDateTime current = LocalDateTime.now(); - LocalDateTime a = start.plusDays(1); - Assertions.assertEquals("1d", NumberUtils.getElapsedTime(start, a)); + LocalDateTime a = current.minusDays(1); + Assertions.assertEquals("1d", NumberUtils.getElapsedTime(current, a)); - LocalDateTime b = start.plusHours(25); - Assertions.assertEquals("1d 1h", NumberUtils.getElapsedTime(start, b)); + LocalDateTime b = current.minusHours(25); + Assertions.assertEquals("1d 1h", NumberUtils.getElapsedTime(current, b)); - LocalDateTime c = start.plusHours(1); - Assertions.assertEquals("1h", NumberUtils.getElapsedTime(start, c)); + LocalDateTime c = current.minusHours(1); + Assertions.assertEquals("1h", NumberUtils.getElapsedTime(current, c)); - LocalDateTime d = start.plusMinutes(12); - Assertions.assertEquals("< 1h", NumberUtils.getElapsedTime(start, d)); + LocalDateTime d = current.minusMinutes(12); + Assertions.assertEquals("< 1h", NumberUtils.getElapsedTime(current, d)); } @Test @@ -75,4 +75,22 @@ class TestNumberUtils { Assertions.assertEquals("5.25", NumberUtils.roundDecimalNumber(5.249999999999)); } + @Test + @DisplayName("Test compact decimals") + void testCompactDecimals() { + Assertions.assertEquals("-40.2", NumberUtils.getCompactDouble(-40.2)); + Assertions.assertEquals("1.23", NumberUtils.getCompactDouble(1.234546)); + Assertions.assertEquals("999", NumberUtils.getCompactDouble(999.0)); + Assertions.assertEquals("1K", NumberUtils.getCompactDouble(1000.0)); + Assertions.assertEquals("2.5K", NumberUtils.getCompactDouble(2500.0)); + Assertions.assertEquals("720K", NumberUtils.getCompactDouble(720000.0)); + Assertions.assertEquals("1M", NumberUtils.getCompactDouble(1000000.0)); + Assertions.assertEquals("40M", NumberUtils.getCompactDouble(40000000.0)); + Assertions.assertEquals("1B", NumberUtils.getCompactDouble(1000000000.0)); + Assertions.assertEquals("1.23B", NumberUtils.getCompactDouble(1230000000.0)); + Assertions.assertEquals("1T", NumberUtils.getCompactDouble(1000000000000.0)); + Assertions.assertEquals("1Q", NumberUtils.getCompactDouble(1000000000000000.0)); + Assertions.assertEquals("-2Q", NumberUtils.getCompactDouble(-2000000000000000.0)); + } + }