diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 4c01fbb45a..2f44418cc0 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -50,4 +50,4 @@ jobs: prerelease: true title: "Slimefun 自动构建版本" files: | - target/Slimefun-v4.9-*.jar + target/Slimefun-*.jar diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index 4f9a3c58c4..8b5b10a7c2 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -4,7 +4,7 @@ me.qoomon maven-git-versioning-extension - 7.2.3 + 7.3.0 \ No newline at end of file diff --git a/.mvn/maven-git-versioning-extension.xml b/.mvn/maven-git-versioning-extension.xml index 8334857058..45512d6bb7 100644 --- a/.mvn/maven-git-versioning-extension.xml +++ b/.mvn/maven-git-versioning-extension.xml @@ -3,18 +3,18 @@ master - 4.9-canary-${commit.short} + ${commit.short}-canary release - 4.9-release-${commit.timestamp.year}.${commit.timestamp.month} + ${commit.timestamp.year}.${commit.timestamp.month}-release test/(.+) - 4.9-${1}-${commit.short} + ${1}-${commit.short} - v([0-9].*)> + ([0-9].*)> ${1} diff --git a/CHANGELOG.md b/CHANGELOG.md index 40a83931b3..c9f229ad6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Table of contents -- [Release Candidate 31 (TBD)](#release-candidate-31-tbd) +- [Release Candidate 32 (TBD)](#release-candidate-32-tbd) +- [Release Candidate 31 (14 Mar 2022)](#release-candidate-31-14-mar-2022) - [Release Candidate 30 (31 Dec 2021)](#release-candidate-30-31-dec-2021) - [Release Candidate 29 (07 Nov 2021)](#release-candidate-29-07-nov-2021) - [Release Candidate 28 (06 Sep 2021)](#release-candidate-28-06-sep-2021) @@ -31,7 +32,19 @@ - [Release Candidate 2 (29 Sep 2019)](#release-candidate-2-29-sep-2019) - [Release Candidate 1 (26 Sep 2019)](#release-candidate-1-26-sep-2019) -## Release Candidate 31 (TBD) + +## Release Candidate 32 (TBD) + +#### Additions + +#### Changes + +#### Fixes +* Fixed #3445 +* Fixed #3504 + +## Release Candidate 31 (14 Mar 2022) +https://thebusybiscuit.github.io/builds/TheBusyBiscuit/Slimefun4/stable/#31 #### Additions * Added Armored Jetpack @@ -49,6 +62,7 @@ * You can now pick up Slimefun blocks in creative mode using the middle mouse button * `/sf search` no longer shows items in hidden item groups (can be overidden by a config setting) * Fluid Pumps can now fill bottles with water +* (API) Added Shulker boxes to `ColoredMaterial` enum #### Changes * (API) `BiomeMapParser` is now `public` diff --git a/jitpack.yml b/jitpack.yml deleted file mode 100644 index 3dcd4f7383..0000000000 --- a/jitpack.yml +++ /dev/null @@ -1,6 +0,0 @@ -before_install: - - sdk install java 17.0.1-open - - sdk use java 17.0.1-open - -jdk: - - openjdk17 diff --git a/pom.xml b/pom.xml index d65ebce3de..8d4d75b88f 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.3.0 @@ -169,7 +169,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.0.0-M6 org.junit.jupiter:junit-jupiter @@ -245,7 +245,7 @@ com.konghq unirest-java - 3.13.6 + 3.13.7 compile @@ -273,7 +273,7 @@ com.github.seeseemelk MockBukkit-v1.18 - 1.18.1 + 1.24.1 test @@ -318,7 +318,7 @@ com.gmail.nossr50.mcMMO mcMMO - 2.1.209 + 2.1.211 provided @@ -360,7 +360,7 @@ com.github.LoneDev6 itemsadder-api - 3.0.0 + 3.0.5 provided @@ -415,7 +415,7 @@ org.maxgamer QuickShop - 5.1.0.3 + 5.1.0.6 provided diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemGroup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemGroup.java index c4d89d64f5..66b9b78fa1 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemGroup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/ItemGroup.java @@ -90,8 +90,7 @@ public ItemGroup(NamespacedKey key, ItemStack item, int tier) { } @Override - @Nonnull - public final NamespacedKey getKey() { + public final @Nonnull NamespacedKey getKey() { return key; } @@ -117,6 +116,16 @@ public void register(@Nonnull SlimefunAddon addon) { sortCategoriesByTier(); } + /** + * This method returns whether this {@link ItemGroup} has been registered yet. + * More specifically: Whether {@link #register(SlimefunAddon)} was called or not. + * + * @return Whether this {@link ItemGroup} has been registered + */ + public boolean isRegistered() { + return this.addon != null && Slimefun.getRegistry().getAllItemGroups().contains(this); + } + /** * Returns the tier of this {@link ItemGroup}. * The tier determines the position of this {@link ItemGroup} in the {@link SlimefunGuide}. @@ -157,8 +166,7 @@ private void sortCategoriesByTier() { * * @return The {@link SlimefunAddon} or null if unregistered */ - @Nullable - public final SlimefunAddon getAddon() { + public final @Nullable SlimefunAddon getAddon() { return addon; } @@ -176,6 +184,10 @@ public void add(@Nonnull SlimefunItem item) { return; } + if (isRegistered() && !item.getAddon().getName().equals(this.addon.getName())) { + item.warn("This item does not belong into ItemGroup " + this + " as that group belongs to " + this.addon.getName()); + } + items.add(item); } @@ -199,8 +211,7 @@ public void remove(@Nonnull SlimefunItem item) { * * @return A localized display item for this {@link ItemGroup} */ - @Nonnull - public ItemStack getItem(@Nonnull Player p) { + public @Nonnull ItemStack getItem(@Nonnull Player p) { return new CustomItemStack(item, meta -> { String name = Slimefun.getLocalization().getItemGroupName(p, getKey()); @@ -224,8 +235,7 @@ public ItemStack getItem(@Nonnull Player p) { * * @return The unlocalized name of this {@link ItemGroup} */ - @Nonnull - public String getUnlocalizedName() { + public @Nonnull String getUnlocalizedName() { return ChatColor.stripColor(item.getItemMeta().getDisplayName()); } @@ -238,8 +248,7 @@ public String getUnlocalizedName() { * * @return The localized name of this {@link ItemGroup} */ - @Nonnull - public String getDisplayName(@Nonnull Player p) { + public @Nonnull String getDisplayName(@Nonnull Player p) { String localized = Slimefun.getLocalization().getItemGroupName(p, getKey()); if (localized != null) { @@ -254,8 +263,7 @@ public String getDisplayName(@Nonnull Player p) { * * @return the list of SlimefunItems bound to this {@link ItemGroup} */ - @Nonnull - public List getItems() { + public @Nonnull List getItems() { return items; } @@ -271,6 +279,52 @@ public boolean contains(@Nullable SlimefunItem item) { return item != null && items.contains(item); } + /** + * This method returns whether this {@link ItemGroup} can be accessed + * by the given {@link Player}. If an {@link ItemGroup} is not accessible, + * it will not show up in the {@link SlimefunGuide} nor will items from this + * {@link ItemGroup} show up in the guide search. + * + * @param p + * The {@link Player} to check for + * + * @return Whether this {@link ItemGroup} is accessible by the given {@link Player} + */ + public boolean isAccessible(@Nonnull Player p) { + return true; + } + + /** + * This method returns whether this {@link ItemGroup} can be viewed + * by the given {@link Player}. Empty {@link ItemGroup ItemGroups} will not + * be visible. This includes {@link ItemGroup ItemGroups} where every {@link SlimefunItem} + * is disabled. If an {@link ItemGroup} is not accessible by the {@link Player}, + * see {@link #isAccessible(Player)}, this method will also return false. + * + * @param p + * The {@link Player} to check for + * + * @return Whether this {@link ItemGroup} is visible to the given {@link Player} + */ + public boolean isVisible(@Nonnull Player p) { + if (items.isEmpty() || !isAccessible(p)) { + return false; + } + + for (SlimefunItem slimefunItem : getItems()) { + /* + * If any item for this item group is visible, + * the item group itself is also visible. + * Empty item groups are not displayed. + */ + if (!slimefunItem.isHidden() && !slimefunItem.isDisabledIn(p.getWorld())) { + return true; + } + } + + return false; + } + @Override public final boolean equals(Object obj) { if (obj instanceof ItemGroup) { @@ -301,24 +355,9 @@ public String toString() { * * @return Whether this {@link ItemGroup} will be hidden to the given {@link Player} */ + @Deprecated public boolean isHidden(@Nonnull Player p) { - for (SlimefunItem slimefunItem : getItems()) { - if (!slimefunItem.isHidden() && !slimefunItem.isDisabledIn(p.getWorld())) { - return false; - } - } - - return true; - } - - /** - * This method returns whether this {@link ItemGroup} has been registered yet. - * More specifically: Whether {@link #register(SlimefunAddon)} was called or not. - * - * @return Whether this {@link ItemGroup} has been registered - */ - public boolean isRegistered() { - return Slimefun.getRegistry().getAllItemGroups().contains(this); + return !isVisible(p); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/FlexItemGroup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/FlexItemGroup.java index 56d065b8b7..92f137dcfc 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/FlexItemGroup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/FlexItemGroup.java @@ -36,6 +36,16 @@ protected FlexItemGroup(NamespacedKey key, ItemStack item, int tier) { super(key, item, tier); } + @Override + public final boolean isVisible(@Nonnull Player p) { + /* + * We can stop this method right here. + * We provide a custom method with more parameters for this. + * See isVisible(...) + */ + return true; + } + /** * This method returns whether this {@link FlexItemGroup} is visible under the given context. * Implementing this method gives full flexibility over who can see the ItemGroup when and where. @@ -66,16 +76,6 @@ protected FlexItemGroup(NamespacedKey key, ItemStack item, int tier) { */ public abstract void open(Player p, PlayerProfile profile, SlimefunGuideMode layout); - @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(...) - */ - return false; - } - @Override public final void add(@Nonnull SlimefunItem item) { throw new UnsupportedOperationException("You cannot add items to a FlexItemGroup!"); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SeasonalItemGroup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SeasonalItemGroup.java index be726b119d..c61212c265 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SeasonalItemGroup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SeasonalItemGroup.java @@ -56,12 +56,12 @@ public SeasonalItemGroup(NamespacedKey key, Month month, int tier, ItemStack ite } @Override - public boolean isHidden(@Nonnull Player p) { - // Hide this ItemGroup if the month differs + public boolean isAccessible(@Nonnull Player p) { + // Block this ItemGroup if the month differs if (month != LocalDate.now().getMonth()) { - return true; + return false; } - return super.isHidden(p); + return super.isAccessible(p); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SubItemGroup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SubItemGroup.java index 0505123228..4797867b35 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SubItemGroup.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/items/groups/SubItemGroup.java @@ -40,14 +40,29 @@ public SubItemGroup(NamespacedKey key, NestedItemGroup parent, ItemStack item, i } @Override - public final boolean isHidden(@Nonnull Player p) { + public final boolean isVisible(@Nonnull Player p) { /* * Sub Categories are always hidden, * they won't show up in the normal guide view. */ + return false; + } + + @Override + public final boolean isAccessible(@Nonnull Player p) { + /* + * Sub Categories are accessible, they are invisible + * but their items are available to the guide search. + */ return true; } + /** + * This method returns the parent {@link NestedItemGroup} which this + * {@link SubItemGroup} belongs to. + * + * @return The parent {@link NestedItemGroup} + */ public final @Nonnull NestedItemGroup getParent() { return parentItemGroup; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java index 5d942114f7..883447d7e4 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java @@ -76,7 +76,7 @@ public PlayerBackpack(@Nonnull PlayerProfile profile, int id, int size) { cfg.setValue(CONFIG_PREFIX + id + ".size", size); markDirty(); - inventory = Bukkit.createInventory(null, size, "Backpack [" + size + " Slots]"); + inventory = Bukkit.createInventory(null, size, "背包 [大小 " + size + "]"); } /** @@ -146,7 +146,7 @@ public void setSize(int size) { this.size = size; cfg.setValue(CONFIG_PREFIX + id + ".size", size); - Inventory inv = Bukkit.createInventory(null, size, "Backpack [" + size + " Slots]"); + Inventory inv = Bukkit.createInventory(null, size, "背包 [大小 " + size + "]"); for (int slot = 0; slot < this.inventory.getSize(); slot++) { inv.setItem(slot, this.inventory.getItem(slot)); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DistinctiveItem.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DistinctiveItem.java new file mode 100644 index 0000000000..35f2423515 --- /dev/null +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/DistinctiveItem.java @@ -0,0 +1,31 @@ +package io.github.thebusybiscuit.slimefun4.core.attributes; + +import org.bukkit.inventory.meta.ItemMeta; + +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; + +import javax.annotation.Nonnull; + +/** + * Implement this interface for any {@link SlimefunItem} to prevent + * cargo using only its ID when comparing. #canStack is used when + * comparing stacks + * + * @author Sefiraat + */ +public interface DistinctiveItem extends ItemAttribute { + + /** + * This method is called by {@link SlimefunUtils#isItemSimilar} when two {@link SlimefunItemStack} + * IDs match on a DistinctiveItem and should return if the two items can stack + * with one another. + * + * @param itemMetaOne + * The {@link ItemMeta} of the first stack being compared. + * @param itemMetaTwo + * The {@link ItemMeta} of the second stack being compared. + * + * @return Whether the two {@link ItemMeta}s are distinct + */ + boolean canStack(@Nonnull ItemMeta itemMetaOne, @Nonnull ItemMeta itemMetaTwo); +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideOption.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideOption.java index 32fbd43d40..1caeb02826 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideOption.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideOption.java @@ -1,14 +1,32 @@ package io.github.thebusybiscuit.slimefun4.core.guide.options; -import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; +import java.util.Optional; + +import javax.annotation.Nonnull; + import org.bukkit.Keyed; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import java.util.Optional; - +import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; +import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide; + +/** + * This interface represents an option in the {@link SlimefunGuide}. + * + * @author TheBusyBiscuit + * + * @param + * The type of value for this option + */ public interface SlimefunGuideOption extends Keyed { + /** + * This returns the {@link SlimefunAddon} which added this {@link SlimefunGuideOption}. + * + * @return The registering {@link SlimefunAddon} + */ + @Nonnull SlimefunAddon getAddon(); Optional getDisplayItem(Player p, ItemStack guide); @@ -19,4 +37,4 @@ public interface SlimefunGuideOption extends Keyed { void setSelectedOption(Player p, ItemStack guide, T value); -} \ No newline at end of file +} diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java index 6a6c6cc3a9..0ae959ebc8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/SurvivalSlimefunGuide.java @@ -33,7 +33,6 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.groups.FlexItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.groups.LockedItemGroup; -import io.github.thebusybiscuit.slimefun4.api.items.groups.SubItemGroup; import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.api.researches.Research; @@ -367,7 +366,7 @@ public void openSearch(PlayerProfile profile, String input, boolean addToHistory break; } - if (!slimefunItem.isHidden() && !isItemGroupHidden(p, slimefunItem) && isSearchFilterApplicable(slimefunItem, searchTerm)) { + if (!slimefunItem.isHidden() && isItemGroupAccessible(p, slimefunItem) && isSearchFilterApplicable(slimefunItem, searchTerm)) { ItemStack itemstack = new CustomItemStack(slimefunItem.getItem(), meta -> { ItemGroup itemGroup = slimefunItem.getItemGroup(); meta.setLore(Arrays.asList("", ChatColor.DARK_GRAY + "\u21E8 " + ChatColor.WHITE + itemGroup.getDisplayName(p))); @@ -397,19 +396,8 @@ public void openSearch(PlayerProfile profile, String input, boolean addToHistory } @ParametersAreNonnullByDefault - private boolean isItemGroupHidden(Player p, SlimefunItem slimefunItem) { - if (showHiddenItemGroupsInSearch) { - return false; - } - - ItemGroup itemGroup = slimefunItem.getItemGroup(); - - // Fixes #3487 - SubItemGroups are "pseudo-hidden" - if (itemGroup instanceof SubItemGroup) { - return false; - } else { - return itemGroup.isHidden(p); - } + private boolean isItemGroupAccessible(Player p, SlimefunItem slimefunItem) { + return showHiddenItemGroupsInSearch || slimefunItem.getItemGroup().isAccessible(p); } @ParametersAreNonnullByDefault diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SlimefunBackpack.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SlimefunBackpack.java index 4ba451149b..4f2a0e3500 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SlimefunBackpack.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SlimefunBackpack.java @@ -6,16 +6,19 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; +import io.github.thebusybiscuit.slimefun4.core.attributes.DistinctiveItem; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.listeners.BackpackListener; +import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; /** @@ -28,7 +31,7 @@ * @see PlayerBackpack * */ -public class SlimefunBackpack extends SimpleSlimefunItem { +public class SlimefunBackpack extends SimpleSlimefunItem implements DistinctiveItem { private final int size; @@ -82,4 +85,14 @@ public ItemUseHandler getItemHandler() { }; } + @Override + public boolean canStack(@Nonnull ItemMeta itemMetaOne, @Nonnull ItemMeta itemMetaTwo) { + boolean hasLoreItem = itemMetaTwo.hasLore(); + boolean hasLoreSfItem = itemMetaOne.hasLore(); + + if (hasLoreItem && hasLoreSfItem && SlimefunUtils.equalsLore(itemMetaTwo.getLore(), itemMetaOne.getLore())) { + return true; + } + return !hasLoreItem && !hasLoreSfItem; + } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/HologramProjector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/HologramProjector.java index f1d00f3471..c7f3c0e364 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/HologramProjector.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/HologramProjector.java @@ -55,14 +55,13 @@ public HologramProjector(ItemGroup itemGroup, SlimefunItemStack item, RecipeType addItemHandler(onPlace(), onRightClick(), onBreak()); } - @Nonnull - private BlockPlaceHandler onPlace() { + private @Nonnull BlockPlaceHandler onPlace() { return new BlockPlaceHandler(false) { @Override public void onPlayerPlace(BlockPlaceEvent e) { Block b = e.getBlockPlaced(); - BlockStorage.addBlockInfo(b, "text", "编辑展示的文字"); + BlockStorage.addBlockInfo(b, "text", "Edit me via the Projector"); BlockStorage.addBlockInfo(b, OFFSET_PARAMETER, "0.5"); BlockStorage.addBlockInfo(b, "owner", e.getPlayer().getUniqueId().toString()); @@ -72,8 +71,7 @@ public void onPlayerPlace(BlockPlaceEvent e) { }; } - @Nonnull - private BlockBreakHandler onBreak() { + private @Nonnull BlockBreakHandler onBreak() { return new SimpleBlockBreakHandler() { @Override @@ -83,8 +81,7 @@ public void onBlockBreak(@Nonnull Block b) { }; } - @Nonnull - public BlockUseHandler onRightClick() { + public @Nonnull BlockUseHandler onRightClick() { return e -> { e.cancel(); @@ -97,15 +94,22 @@ public BlockUseHandler onRightClick() { }; } - private static void openEditor(@Nonnull Player p, @Nonnull Block projector) { + private void openEditor(@Nonnull Player p, @Nonnull Block projector) { ChestMenu menu = new ChestMenu(Slimefun.getLocalization().getMessage(p, "machines.HOLOGRAM_PROJECTOR.inventory-title")); - menu.addItem(0, new CustomItemStack(Material.NAME_TAG, "&7展示文本 &e(点击编辑)", "", "&r" + ChatColors.color(BlockStorage.getLocationInfo(projector.getLocation(), "text")))); + menu.addItem(0, new CustomItemStack(Material.NAME_TAG, "&7展示文本 &e(点击编辑)", "", "&f" + ChatColors.color(BlockStorage.getLocationInfo(projector.getLocation(), "text")))); menu.addMenuClickHandler(0, (pl, slot, item, action) -> { pl.closeInventory(); Slimefun.getLocalization().sendMessage(pl, "machines.HOLOGRAM_PROJECTOR.enter-text", true); ChatUtils.awaitInput(pl, message -> { + // Fixes #3445 - Make sure the projector is not broken + if (!BlockStorage.check(projector, getId())) { + // Hologram projector no longer exists. + // TODO: Add a chat message informing the player that their message was ignored. + return; + } + ArmorStand hologram = getArmorStand(projector, true); hologram.setCustomName(ChatColors.color(message)); BlockStorage.addBlockInfo(projector, "text", hologram.getCustomName()); @@ -115,7 +119,7 @@ private static void openEditor(@Nonnull Player p, @Nonnull Block projector) { return false; }); - menu.addItem(1, new CustomItemStack(Material.CLOCK, "&7高度: &e" + NumberUtils.reparseDouble(Double.valueOf(BlockStorage.getLocationInfo(projector.getLocation(), OFFSET_PARAMETER)) + 1.0D), "", "&r左击: &7+0.1", "&r右击: &7-0.1")); + menu.addItem(1, new CustomItemStack(Material.CLOCK, "&7高度: &e" + NumberUtils.reparseDouble(Double.valueOf(BlockStorage.getLocationInfo(projector.getLocation(), OFFSET_PARAMETER)) + 1.0D), "", "&f左键单击: &7+0.1", "&f右键单击: &7-0.1")); menu.addMenuClickHandler(1, (pl, slot, item, action) -> { double offset = NumberUtils.reparseDouble(Double.valueOf(BlockStorage.getLocationInfo(projector.getLocation(), OFFSET_PARAMETER)) + (action.isRightClicked() ? -0.1F : 0.1F)); ArmorStand hologram = getArmorStand(projector, true); @@ -154,8 +158,7 @@ private static ArmorStand getArmorStand(@Nonnull Block projector, boolean create return hologram; } - @Nonnull - private static ArmorStand spawnArmorStand(@Nonnull Location l) { + private static @Nonnull ArmorStand spawnArmorStand(@Nonnull Location l) { ArmorStand armorStand = (ArmorStand) l.getWorld().spawnEntity(l, EntityType.ARMOR_STAND); armorStand.setVisible(false); armorStand.setSilent(true); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/EnhancedCraftingTable.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/EnhancedCraftingTable.java index 7a0bb1a3b6..b799e420e7 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 @@ -91,9 +91,9 @@ private void craft(Inventory inv, Block dispenser, Player p, Block b, ItemStack private boolean isCraftable(Inventory inv, ItemStack[] recipe) { for (int j = 0; j < inv.getContents().length; j++) { - if (!SlimefunUtils.isItemSimilar(inv.getContents()[j], recipe[j], true)) { + if (!SlimefunUtils.isItemSimilar(inv.getContents()[j], recipe[j], true, true, false)) { if (SlimefunItem.getByItem(recipe[j]) instanceof SlimefunBackpack) { - if (!SlimefunUtils.isItemSimilar(inv.getContents()[j], recipe[j], false)) { + if (!SlimefunUtils.isItemSimilar(inv.getContents()[j], recipe[j], false, true, false)) { return false; } } else { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/HerculesPickaxe.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/HerculesPickaxe.java index a2cfc1b8c8..a5b9eac614 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/HerculesPickaxe.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/HerculesPickaxe.java @@ -4,6 +4,7 @@ import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; import org.bukkit.inventory.ItemStack; import io.github.bakedlibs.dough.items.CustomItemStack; @@ -30,35 +31,33 @@ public HerculesPickaxe(ItemGroup itemGroup, SlimefunItemStack item, RecipeType r Material mat = e.getBlock().getType(); if (SlimefunTag.ORES.isTagged(mat)) { + if (tool.getEnchantments().containsKey(Enchantment.SILK_TOUCH)) { + return; + } + if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_17)) { switch (mat) { case DEEPSLATE_IRON_ORE: drops.add(new CustomItemStack(SlimefunItems.IRON_DUST, 2)); - break; + return; case DEEPSLATE_GOLD_ORE: drops.add(new CustomItemStack(SlimefunItems.GOLD_DUST, 2)); - break; + return; case COPPER_ORE: case DEEPSLATE_COPPER_ORE: drops.add(new CustomItemStack(SlimefunItems.COPPER_DUST, 2)); - break; + return; default: - break; + return; } } switch (mat) { case IRON_ORE: drops.add(new CustomItemStack(SlimefunItems.IRON_DUST, 2)); - break; + return; case GOLD_ORE: drops.add(new CustomItemStack(SlimefunItems.GOLD_DUST, 2)); - break; - default: - for (ItemStack drop : e.getBlock().getDrops(tool)) { - drops.add(new CustomItemStack(drop, drop.getAmount() * 2)); - } - break; } } }; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BeeWingsListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BeeWingsListener.java index 245710939d..f79a74349d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BeeWingsListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BeeWingsListener.java @@ -36,7 +36,8 @@ public BeeWingsListener(@Nonnull Slimefun plugin, @Nonnull BeeWings wings) { @EventHandler(ignoreCancelled = true) public void onApproachGround(EntityToggleGlideEvent e) { - if (!e.isGliding() || wings.isDisabled() || !(e.getEntity() instanceof Player)) { + + if (wings == null || !e.isGliding() || wings.isDisabled() || !(e.getEntity() instanceof Player)) { return; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TalismanListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TalismanListener.java index 207176f9ba..ef2af27a75 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TalismanListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/TalismanListener.java @@ -21,6 +21,7 @@ import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; +import org.bukkit.entity.Ravager; import org.bukkit.entity.Trident; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; @@ -201,6 +202,15 @@ private Collection getExtraDrops(LivingEntity entity, Collection item.getType() == Material.SADDLE); + } + /* * WARNING: This check is broken as entities now set their * equipment to NULL before calling the event! 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 237b4be417..9c5c70098a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterial.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterial.java @@ -173,6 +173,28 @@ public enum ColoredMaterial { Material.GREEN_CONCRETE, Material.RED_CONCRETE, Material.BLACK_CONCRETE + }), + + /** + * This {@link List} contains all shulker box colors ordered by their appearance ingame. + */ + SHULKER_BOX(new Material[] { + Material.WHITE_SHULKER_BOX, + Material.ORANGE_SHULKER_BOX, + Material.MAGENTA_SHULKER_BOX, + Material.LIGHT_BLUE_SHULKER_BOX, + Material.YELLOW_SHULKER_BOX, + Material.LIME_SHULKER_BOX, + Material.PINK_SHULKER_BOX, + Material.GRAY_SHULKER_BOX, + Material.LIGHT_GRAY_SHULKER_BOX, + Material.CYAN_SHULKER_BOX, + Material.PURPLE_SHULKER_BOX, + Material.BLUE_SHULKER_BOX, + Material.BROWN_SHULKER_BOX, + Material.GREEN_SHULKER_BOX, + Material.RED_SHULKER_BOX, + Material.BLACK_SHULKER_BOX }); // @formatter:on 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 337a0c2be8..96db7b95a3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java @@ -37,6 +37,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.ItemSpawnReason; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; +import io.github.thebusybiscuit.slimefun4.core.attributes.DistinctiveItem; import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactive; import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound; import io.github.thebusybiscuit.slimefun4.core.debug.Debug; @@ -265,10 +266,14 @@ public static boolean containsSimilarItem(Inventory inventory, ItemStack item, b } public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStack sfitem, boolean checkLore) { - return isItemSimilar(item, sfitem, checkLore, true); + return isItemSimilar(item, sfitem, checkLore, true, true); } public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStack sfitem, boolean checkLore, boolean checkAmount) { + return isItemSimilar(item, sfitem, checkLore, checkAmount, true); + } + + public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStack sfitem, boolean checkLore, boolean checkAmount, boolean checkDistinctiveItem) { if (item == null) { return sfitem == null; } else if (sfitem == null) { @@ -277,17 +282,44 @@ public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStac return false; } else if (checkAmount && item.getAmount() < sfitem.getAmount()) { return false; - } else if (sfitem instanceof SlimefunItemStack && item instanceof SlimefunItemStack) { - return ((SlimefunItemStack) item).getItemId().equals(((SlimefunItemStack) sfitem).getItemId()); + } else if (checkDistinctiveItem && sfitem instanceof SlimefunItemStack && item instanceof SlimefunItemStack) { + SlimefunItemStack stackOne = (SlimefunItemStack) sfitem; + SlimefunItemStack stackTwo = (SlimefunItemStack) item; + if (stackOne.getItemId().equals(stackTwo.getItemId())) { + /* + * PR #3417 + * + * Some items can't rely on just IDs matching and will implement Distinctive Item + * in which case we want to use the method provided to compare + */ + if (stackOne instanceof DistinctiveItem && stackTwo instanceof DistinctiveItem) { + return ((DistinctiveItem) stackOne).canStack(stackOne.getItemMeta(), stackTwo.getItemMeta()); + } + return true; + } + return false; } else if (item.hasItemMeta()) { Debug.log(TestCase.CARGO_INPUT_TESTING, "SlimefunUtils#isItemSimilar - item.hasItemMeta()"); ItemMeta itemMeta = item.getItemMeta(); if (sfitem instanceof SlimefunItemStack) { - Optional id = Slimefun.getItemDataService().getItemData(itemMeta); - - if (id.isPresent()) { - return id.get().equals(((SlimefunItemStack) sfitem).getItemId()); + String id = Slimefun.getItemDataService().getItemData(itemMeta).orElse(null); + + if (id != null) { + if (checkDistinctiveItem) { + /* + * PR #3417 + * + * Some items can't rely on just IDs matching and will implement Distinctive Item + * in which case we want to use the method provided to compare + */ + Optional optionalDistinctive = getDistinctiveItem(id); + if (optionalDistinctive.isPresent()) { + ItemMeta sfItemMeta = sfitem.getItemMeta(); + return optionalDistinctive.get().canStack(sfItemMeta, itemMeta); + } + } + return id.equals(((SlimefunItemStack) sfitem).getItemId()); } ItemMetaSnapshot meta = ((SlimefunItemStack) sfitem).getItemMetaSnapshot(); @@ -300,12 +332,25 @@ public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStac * Slimefun items may be ItemStackWrapper's in the context of cargo * so let's try to do an ID comparison before meta comparison */ - ItemMeta possibleSfItemMeta = sfitem.getItemMeta(); Debug.log(TestCase.CARGO_INPUT_TESTING, " sfitem is ItemStackWrapper - possible SF Item: {}", sfitem); + ItemMeta possibleSfItemMeta = sfitem.getItemMeta(); + String id = Slimefun.getItemDataService().getItemData(itemMeta).orElse(null); + String possibleItemId = Slimefun.getItemDataService().getItemData(possibleSfItemMeta).orElse(null); // Prioritize SlimefunItem id comparison over ItemMeta comparison - if (Slimefun.getItemDataService().hasEqualItemData(possibleSfItemMeta, itemMeta)) { + if (id != null && id.equals(possibleItemId)) { Debug.log(TestCase.CARGO_INPUT_TESTING, " Item IDs matched!"); + + /* + * PR #3417 + * + * Some items can't rely on just IDs matching and will implement Distinctive Item + * in which case we want to use the method provided to compare + */ + Optional optionalDistinctive = getDistinctiveItem(id); + if (optionalDistinctive.isPresent()) { + return optionalDistinctive.get().canStack(possibleSfItemMeta, itemMeta); + } return true; } else { Debug.log(TestCase.CARGO_INPUT_TESTING, " Item IDs don't match, checking meta {} == {} (lore: {})", itemMeta, possibleSfItemMeta, checkLore); @@ -323,6 +368,14 @@ public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStac } } + private static @Nonnull Optional getDistinctiveItem(@Nonnull String id) { + SlimefunItem slimefunItem = SlimefunItem.getById(id); + if (slimefunItem instanceof DistinctiveItem) { + return Optional.of((DistinctiveItem) slimefunItem); + } + return Optional.empty(); + } + private static boolean equalsItemMeta(@Nonnull ItemMeta itemMeta, @Nonnull ItemMetaSnapshot itemMetaSnapshot, boolean checkLore) { Optional displayName = itemMetaSnapshot.getDisplayName(); @@ -527,7 +580,7 @@ public static boolean canPlayerUseItem(@Nonnull Player p, @Nullable ItemStack it * * @param inventory * The {@link Inventory} to check. - * + * * @return True if the inventory is empty and false otherwise */ public static boolean isInventoryEmpty(@Nonnull Inventory inventory) { diff --git a/src/main/java/ren/natsuyuk1/slimefunextra/UpdateChecker.java b/src/main/java/ren/natsuyuk1/slimefunextra/UpdateChecker.java index 0aa12f6eba..e475537359 100644 --- a/src/main/java/ren/natsuyuk1/slimefunextra/UpdateChecker.java +++ b/src/main/java/ren/natsuyuk1/slimefunextra/UpdateChecker.java @@ -12,6 +12,13 @@ public class UpdateChecker { private static final String UPDATE_CHECK_URL = "https://gitee.com/api/v5/repos/StarWishsama/Slimefun4/releases/latest"; public static void checkUpdate() { + String currentVersion = Slimefun.getVersion(); + + // Only available for release user + if (!currentVersion.contains("release")) { + return; + } + try { HttpResponse response = Unirest.get(UPDATE_CHECK_URL) .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36") @@ -22,12 +29,6 @@ public static void checkUpdate() { if (Slimefun.instance() != null) { String latestVersionCode = node.getObject().getString("tag_name"); - String currentVersion = Slimefun.getVersion(); - - // Only available for release user - if (!currentVersion.contains("release")) { - return; - } String currentYear = latestVersionCode.split("\\.")[0]; String currentMonth = latestVersionCode.split("\\.")[1]; diff --git a/src/main/resources/languages/zh-CN/messages.yml b/src/main/resources/languages/zh-CN/messages.yml index 71eb78dc18..f204de9c6e 100644 --- a/src/main/resources/languages/zh-CN/messages.yml +++ b/src/main/resources/languages/zh-CN/messages.yml @@ -9,6 +9,8 @@ commands: search: 搜索 Slimefun 物品 open_guide: 使用命令打开 Slimefun 指南 stats: 查看玩家的统计数据 + transform: 将物品转换为英文物品 + id: 获取手持物品的 Slimefun ID research: description: 解锁/重置玩家的研究进度 reset: '&c你已重置了 %player% 的研究进度' diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/registration/TestItemGroups.java b/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/registration/TestItemGroups.java index 359473066d..d290992ff9 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/registration/TestItemGroups.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/implementation/registration/TestItemGroups.java @@ -87,7 +87,7 @@ void testHidden() { Player player = server.addPlayer(); // Empty Item Groups are also hidden - Assertions.assertTrue(group.isHidden(player)); + Assertions.assertFalse(group.isVisible(player)); SlimefunItem disabledItem = TestUtilities.mockSlimefunItem(plugin, "DISABLED_ITEM_GROUP_ITEM", new CustomItemStack(Material.BEETROOT, "&4Disabled")); Slimefun.getItemCfg().setValue("DISABLED_ITEM_GROUP_ITEM.enabled", false); @@ -96,7 +96,7 @@ void testHidden() { disabledItem.load(); // A disabled Item should also make the ItemGroup hide - Assertions.assertTrue(group.isHidden(player)); + Assertions.assertFalse(group.isVisible(player)); SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "ITEM_GROUP_HIDDEN_TEST", new CustomItemStack(Material.BAMBOO, "&6Test Bamboo")); item.setItemGroup(group); @@ -105,10 +105,10 @@ void testHidden() { item.load(); // A hidden Item should also make the ItemGroup hide - Assertions.assertTrue(group.isHidden(player)); + Assertions.assertFalse(group.isVisible(player)); item.setHidden(false); - Assertions.assertFalse(group.isHidden(player)); + Assertions.assertTrue(group.isVisible(player)); } @Test @@ -201,11 +201,11 @@ void ItemGroups() { Player player = server.addPlayer(); Assertions.assertEquals(month, group.getMonth()); - Assertions.assertFalse(group.isHidden(player)); + Assertions.assertTrue(group.isVisible(player)); // ItemGroup with future Month SeasonalItemGroup itemGroup2 = new SeasonalItemGroup(group.getKey(), month.plus(6), 1, new CustomItemStack(Material.MILK_BUCKET, "&dSeasonal Test")); - Assertions.assertTrue(itemGroup2.isHidden(player)); + Assertions.assertFalse(itemGroup2.isVisible(player)); } @Test @@ -225,7 +225,7 @@ public boolean isVisible(Player p, PlayerProfile profile, SlimefunGuideMode layo }; Player player = server.addPlayer(); - Assertions.assertFalse(group.isHidden(player)); + Assertions.assertTrue(group.isVisible(player)); Assertions.assertThrows(UnsupportedOperationException.class, () -> group.add(null)); Assertions.assertThrows(UnsupportedOperationException.class, () -> group.contains(null)); diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/utils/TestFireworkUtils.java b/src/test/java/io/github/thebusybiscuit/slimefun4/utils/TestFireworkUtils.java index 302124f0a5..c979f740e2 100644 --- a/src/test/java/io/github/thebusybiscuit/slimefun4/utils/TestFireworkUtils.java +++ b/src/test/java/io/github/thebusybiscuit/slimefun4/utils/TestFireworkUtils.java @@ -7,6 +7,7 @@ import org.bukkit.Color; import org.bukkit.FireworkEffect; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.entity.Firework; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -20,10 +21,13 @@ import be.seeseemelk.mockbukkit.WorldMock; class TestFireworkUtils { + + private static World world; @BeforeAll public static void load() { MockBukkit.mock(); + world = new WorldMock(); } @AfterAll @@ -35,7 +39,7 @@ public static void unload() { @MethodSource("getColors") @DisplayName("Test colored Fireworks") void testColoredFirework(Color color) { - Location l = new Location(new WorldMock(), 50, 50, 50); + Location l = new Location(world, 50, 50, 50); Firework firework = FireworkUtils.createFirework(l, color); Assertions.assertEquals(l, firework.getLocation());