getCurrentlyResearchingPlayers() {
return researchingPlayers;
}
@@ -159,6 +150,10 @@ public class SlimefunRegistry {
return enableResearches;
}
+ public void setFreeCreativeResearchingEnabled(boolean enabled) {
+ freeCreativeResearches = enabled;
+ }
+
public boolean isFreeCreativeResearchingEnabled() {
return freeCreativeResearches;
}
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 3ae4959e9..c8e178e7a 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
@@ -77,4 +77,14 @@ public abstract class FlexCategory extends Category {
throw new UnsupportedOperationException("A FlexCategory has no items!");
}
+ @Override
+ public final boolean contains(SlimefunItem item) {
+ throw new UnsupportedOperationException("A FlexCategory has no items!");
+ }
+
+ @Override
+ public final void remove(SlimefunItem item) {
+ throw new UnsupportedOperationException("A FlexCategory has no items, so there is nothing remove!");
+ }
+
}
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
new file mode 100644
index 000000000..581cae502
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/categories/LockedCategory.java
@@ -0,0 +1,161 @@
+package io.github.thebusybiscuit.slimefun4.core.categories;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.Category;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+import me.mrCookieSlime.Slimefun.api.Slimefun;
+
+/**
+ * Represents a {@link Category} that cannot be opened until the parent category/categories
+ * are fully unlocked.
+ *
+ * See {@link Category} for the complete documentation.
+ *
+ * @author TheBusyBiscuit
+ *
+ * @see Category
+ * @see SeasonalCategory
+ *
+ */
+public class LockedCategory extends Category {
+
+ private final NamespacedKey[] keys;
+ private final Set parents = new HashSet<>();
+
+ /**
+ * The basic constructor for a LockedCategory.
+ * Like {@link Category}, the default tier is automatically set to 3.
+ *
+ * @param key
+ * A unique identifier for this category
+ * @param item
+ * The display item for this category
+ * @param parents
+ * The parent categories for this category
+ *
+ */
+ public LockedCategory(NamespacedKey key, ItemStack item, NamespacedKey... parents) {
+ this(key, item, 3, parents);
+ }
+
+ /**
+ * The constructor for a LockedCategory.
+ *
+ * @param key
+ * A unique identifier for this category
+ * @param item
+ * The display item for this category
+ * @param tier
+ * The tier of this category
+ * @param parents
+ * The parent categories for this category
+ *
+ */
+ public LockedCategory(NamespacedKey key, ItemStack item, int tier, NamespacedKey... parents) {
+ super(key, item, tier);
+ Validate.noNullElements(parents, "A LockedCategory must not have any 'null' parents!");
+
+ this.keys = parents;
+ }
+
+ @Override
+ public void register() {
+ super.register();
+
+ List namespacedKeys = new ArrayList<>();
+
+ for (NamespacedKey key : keys) {
+ if (key != null) {
+ namespacedKeys.add(key);
+ }
+ }
+
+ for (Category category : SlimefunPlugin.getRegistry().getCategories()) {
+ if (namespacedKeys.remove(category.getKey())) {
+ addParent(category);
+ }
+ }
+
+ for (NamespacedKey key : namespacedKeys) {
+ Slimefun.getLogger().log(Level.INFO, "Parent \"{0}\" for Category \"{1}\" was not found, probably just disabled.", new Object[] { key, getKey() });
+ }
+ }
+
+ /**
+ * Gets the list of parent categories for this {@link LockedCategory}.
+ *
+ * @return the list of parent categories
+ *
+ * @see #addParent(Category)
+ * @see #removeParent(Category)
+ */
+ public Set getParents() {
+ return parents;
+ }
+
+ /**
+ * Adds a parent {@link Category} to this {@link LockedCategory}.
+ *
+ * @param category
+ * The {@link Category} to add as a parent
+ *
+ * @see #getParents()
+ * @see #removeParent(Category)
+ */
+ public void addParent(Category category) {
+ if (category == this || category == null) {
+ throw new IllegalArgumentException("Category '" + item.getItemMeta().getDisplayName() + "' cannot be a parent of itself or have a 'null' parent.");
+ }
+
+ parents.add(category);
+ }
+
+ /**
+ * Removes a {@link Category} from the parents of this {@link LockedCategory}.
+ *
+ * @param category
+ * The {@link Category} to remove from the parents of this {@link LockedCategory}
+ *
+ * @see #getParents()
+ * @see #addParent(Category)
+ */
+ public void removeParent(Category category) {
+ parents.remove(category);
+ }
+
+ /**
+ * Checks if the {@link Player} has fully unlocked all parent categories.
+ *
+ * @param p
+ * The {@link Player} to check
+ * @param profile
+ * The {@link PlayerProfile} that belongs to the given {@link Player}
+ * @return Whether the {@link Player} has fully completed all parent categories, otherwise false
+ */
+ public boolean hasUnlocked(Player p, 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
+ if (Slimefun.isEnabled(p, item, false) && Slimefun.hasPermission(p, item, false) && !profile.hasUnlocked(item.getResearch())) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+}
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 b47d12266..dcb3cc516 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
@@ -8,7 +8,6 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import me.mrCookieSlime.Slimefun.Objects.Category;
-import me.mrCookieSlime.Slimefun.Objects.LockedCategory;
/**
* Represents a {@link Category} that is only displayed in the Guide during
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunTabCompleter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunTabCompleter.java
index 5696c41a9..1d97a52f7 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunTabCompleter.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/SlimefunTabCompleter.java
@@ -5,13 +5,12 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
-import java.util.Set;
-import org.bukkit.NamespacedKey;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@@ -35,14 +34,14 @@ class SlimefunTabCompleter implements TabCompleter {
return createReturnList(getSlimefunItems(), args[2]);
}
else if (args[0].equalsIgnoreCase("research")) {
- Set researches = SlimefunPlugin.getRegistry().getResearchIds().keySet();
+ List researches = SlimefunPlugin.getRegistry().getResearches();
List suggestions = new LinkedList<>();
suggestions.add("all");
suggestions.add("reset");
- for (NamespacedKey key : researches) {
- suggestions.add(key.toString().toLowerCase(Locale.ROOT));
+ for (Research research : researches) {
+ suggestions.add(research.getKey().toString().toLowerCase(Locale.ROOT));
}
return createReturnList(suggestions, args[2]);
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/BackpackCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/BackpackCommand.java
new file mode 100644
index 000000000..cbb4c0ac9
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/BackpackCommand.java
@@ -0,0 +1,80 @@
+package io.github.thebusybiscuit.slimefun4.core.commands.subcommands;
+
+import org.bukkit.Bukkit;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.core.commands.SlimefunCommand;
+import io.github.thebusybiscuit.slimefun4.core.commands.SubCommand;
+import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
+import me.mrCookieSlime.Slimefun.api.Slimefun;
+
+class BackpackCommand extends SubCommand {
+
+ BackpackCommand(SlimefunPlugin plugin, SlimefunCommand cmd) {
+ super(plugin, cmd);
+ }
+
+ @Override
+ public String getName() {
+ return "backpack";
+ }
+
+ @Override
+ protected String getDescription() {
+ return "commands.backpack.description";
+ }
+
+ @Override
+ public boolean isHidden() {
+ return false;
+ }
+
+ @Override
+ public void onExecute(CommandSender sender, String[] args) {
+ if (!(sender instanceof Player) || !sender.hasPermission("slimefun.command.backpack")) {
+ SlimefunPlugin.getLocal().sendMessage(sender, "messages.no-permission", true);
+ return;
+ }
+
+ if (args.length != 3) {
+ SlimefunPlugin.getLocal().sendMessage(sender, "messages.usage", true, msg -> msg.replace("%usage%", "/sf backpack "));
+ return;
+ }
+
+ Player p = (Player) sender;
+ if (!PatternUtils.NUMERIC.matcher(args[2]).matches()) {
+ SlimefunPlugin.getLocal().sendMessage(sender, "commands.backpack.invalid-id");
+ return;
+ }
+
+ int id = Integer.parseInt(args[2]);
+
+ @SuppressWarnings("deprecation")
+ OfflinePlayer owner = Bukkit.getOfflinePlayer(args[1]);
+
+ if (!owner.hasPlayedBefore()) {
+ SlimefunPlugin.getLocal().sendMessage(sender, "commands.backpack.player-never-joined");
+ return;
+ }
+
+ PlayerProfile.get(owner, profile -> {
+ if (!profile.getBackpack(id).isPresent()) {
+ SlimefunPlugin.getLocal().sendMessage(sender, "commands.backpack.backpack-does-not-exist");
+ return;
+ }
+
+ Slimefun.runSync(() -> {
+ ItemStack item = SlimefunItems.RESTORED_BACKPACK.clone();
+ SlimefunPlugin.getBackpackListener().setBackpackId(p, item, 2, id);
+ p.getInventory().addItem(item);
+ SlimefunPlugin.getLocal().sendMessage(sender, "commands.backpack.restored-backpack-given");
+ });
+ });
+ }
+}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/Commands.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/Commands.java
index 9e7c5894b..e11282560 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/Commands.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/Commands.java
@@ -8,7 +8,8 @@ import me.mrCookieSlime.Slimefun.SlimefunPlugin;
public final class Commands {
- private Commands() {}
+ private Commands() {
+ }
public static void addCommands(SlimefunCommand cmd, Collection commands) {
SlimefunPlugin plugin = cmd.getPlugin();
@@ -25,5 +26,6 @@ public final class Commands {
commands.add(new OpenGuideCommand(plugin, cmd));
commands.add(new SearchCommand(plugin, cmd));
commands.add(new DebugFishCommand(plugin, cmd));
+ commands.add(new BackpackCommand(plugin, cmd));
}
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ResearchCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ResearchCommand.java
index 2cf63ee00..eeca270f7 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ResearchCommand.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/ResearchCommand.java
@@ -9,8 +9,8 @@ import io.github.thebusybiscuit.cscorelib2.players.PlayerList;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.commands.SlimefunCommand;
import io.github.thebusybiscuit.slimefun4.core.commands.SubCommand;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
-import me.mrCookieSlime.Slimefun.Objects.Research;
class ResearchCommand extends SubCommand {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/GuideHistory.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/GuideHistory.java
index dcb1d3732..7161a7bda 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/GuideHistory.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/GuideHistory.java
@@ -3,6 +3,7 @@ package io.github.thebusybiscuit.slimefun4.core.guide;
import java.util.Deque;
import java.util.LinkedList;
+import org.apache.commons.lang.Validate;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@@ -32,6 +33,7 @@ public class GuideHistory {
* The {@link PlayerProfile} this {@link GuideHistory} was made for
*/
public GuideHistory(PlayerProfile profile) {
+ Validate.notNull(profile, "Cannot create a GuideHistory without a PlayerProfile!");
this.profile = profile;
}
@@ -77,6 +79,7 @@ public class GuideHistory {
* The {@link SlimefunItem} that should be added to this {@link GuideHistory}
*/
public void add(SlimefunItem item) {
+ Validate.notNull(item, "Cannot add a nonexisting SlimefunItem to the GuideHistory!");
queue.add(new GuideEntry<>(item, 0));
}
@@ -87,13 +90,17 @@ public class GuideHistory {
* The term that the {@link Player} searched for
*/
public void add(String searchTerm) {
+ Validate.notNull(searchTerm, "Cannot add an empty Search Term to the GuideHistory!");
queue.add(new GuideEntry<>(searchTerm, 0));
}
private void refresh(T object, int page) {
+ Validate.notNull(object, "Cannot add a null Entry to the GuideHistory!");
+ Validate.isTrue(page >= 0, "page must not be negative!");
+
GuideEntry> lastEntry = getLastEntry(false);
- if (lastEntry != null && lastEntry.getIndexedObject() == object) {
+ if (lastEntry != null && lastEntry.getIndexedObject().equals(object)) {
lastEntry.setPage(page);
}
else {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuide.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuide.java
index 334936907..36e6bd4f4 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuide.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuide.java
@@ -87,7 +87,7 @@ public final class SlimefunGuide {
if (!SlimefunPlugin.getWorldSettingsService().isWorldEnabled(p.getWorld())) {
return;
}
-
+
Optional optional = PlayerProfile.find(p);
if (optional.isPresent()) {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuideImplementation.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuideImplementation.java
index 7875a08fc..71d4a6729 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuideImplementation.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/SlimefunGuideImplementation.java
@@ -5,11 +5,11 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.implementation.guide.BookSlimefunGuide;
import io.github.thebusybiscuit.slimefun4.implementation.guide.ChestSlimefunGuide;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.Category;
-import me.mrCookieSlime.Slimefun.Objects.Research;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.Slimefun;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java
index 6ad707cb1..45e7c5593 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/guide/options/SlimefunGuideSettings.java
@@ -100,7 +100,7 @@ public final class SlimefunGuideSettings {
});
if (SlimefunPlugin.getUpdater().getBranch().isOfficial()) {
- menu.addItem(49, new CustomItem(Material.REDSTONE_TORCH, "&4" + SlimefunPlugin.getLocal().getMessage(p, "guide.title.bugs"), "", "&7&oBug reports have to be made in English!", "", "&7Open Issues: &a" + SlimefunPlugin.getGitHubService().getIssues(), "&7Pending Pull Requests: &a" + SlimefunPlugin.getGitHubService().getPullRequests(), "", "&7\u21E8 &eClick to go to the Slimefun4 Bug Tracker"), (pl, slot, item, action) -> {
+ menu.addItem(49, new CustomItem(Material.REDSTONE_TORCH, "&4" + SlimefunPlugin.getLocal().getMessage(p, "guide.title.bugs"), "", "&7&oBug reports have to be made in English!", "", "&7Open Issues: &a" + SlimefunPlugin.getGitHubService().getOpenissues(), "&7Pending Pull Requests: &a" + SlimefunPlugin.getGitHubService().getPendingPullRequests(), "", "&7\u21E8 &eClick to go to the Slimefun4 Bug Tracker"), (pl, slot, item, action) -> {
pl.closeInventory();
ChatUtils.sendURL(pl, "https://github.com/TheBusyBiscuit/Slimefun4/issues");
return false;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/NetworkManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java
similarity index 77%
rename from src/main/java/io/github/thebusybiscuit/slimefun4/api/network/NetworkManager.java
rename to src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java
index f64ba7101..bb7bccfea 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/NetworkManager.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/NetworkManager.java
@@ -1,13 +1,16 @@
-package io.github.thebusybiscuit.slimefun4.api.network;
+package io.github.thebusybiscuit.slimefun4.core.networks;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
+import org.apache.commons.lang.Validate;
import org.bukkit.Location;
import org.bukkit.Server;
import io.github.thebusybiscuit.cscorelib2.config.Config;
+import io.github.thebusybiscuit.slimefun4.api.network.Network;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener;
/**
@@ -20,7 +23,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListen
* @see NetworkListener
*
*/
-public final class NetworkManager {
+public class NetworkManager {
private final int maxNodes;
private final List networks = new LinkedList<>();
@@ -28,11 +31,12 @@ public final class NetworkManager {
/**
* This creates a new {@link NetworkManager} with the given capacity.
*
- * @param capacity
+ * @param maxStepSize
* The maximum amount of nodes a {@link Network} can have
*/
- public NetworkManager(int capacity) {
- maxNodes = capacity;
+ public NetworkManager(int maxStepSize) {
+ Validate.isTrue(maxStepSize > 0, "The maximal Network size must be above zero!");
+ maxNodes = maxStepSize;
}
/**
@@ -54,14 +58,14 @@ public final class NetworkManager {
return networks;
}
- public T getNetworkFromLocation(Location l, Class type) {
+ public Optional getNetworkFromLocation(Location l, Class type) {
for (Network network : networks) {
if (type.isInstance(network) && network.connectsTo(l)) {
- return type.cast(network);
+ return Optional.of(type.cast(network));
}
}
- return null;
+ return Optional.empty();
}
public List getNetworksFromLocation(Location l, Class type) {
@@ -86,7 +90,7 @@ public final class NetworkManager {
public void handleAllNetworkLocationUpdate(Location l) {
for (Network n : getNetworksFromLocation(l, Network.class)) {
- n.handleLocationUpdate(l);
+ n.markDirty(l);
}
}
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 39c685f83..e773bb3f1 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
@@ -36,18 +36,20 @@ public class CargoNet extends ChestTerminalNetwork {
private int tickDelayThreshold = 0;
public static CargoNet getNetworkFromLocation(Location l) {
- return SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, CargoNet.class);
+ return SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, CargoNet.class).orElse(null);
}
public static CargoNet getNetworkFromLocationOrCreate(Location l) {
- CargoNet cargoNetwork = getNetworkFromLocation(l);
+ Optional cargoNetwork = SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, CargoNet.class);
- if (cargoNetwork == null) {
- cargoNetwork = new CargoNet(l);
- SlimefunPlugin.getNetworkManager().registerNetwork(cargoNetwork);
+ if (cargoNetwork.isPresent()) {
+ return cargoNetwork.get();
+ }
+ else {
+ CargoNet network = new CargoNet(l);
+ SlimefunPlugin.getNetworkManager().registerNetwork(network);
+ return network;
}
-
- return cargoNetwork;
}
protected CargoNet(Location l) {
@@ -210,57 +212,10 @@ public class CargoNet extends ChestTerminalNetwork {
// (Apart from ChestTerminal Buses)
for (Map.Entry entry : inputs.entrySet()) {
Location input = entry.getKey();
-
Optional attachedBlock = getAttachedBlock(input.getBlock());
- if (!attachedBlock.isPresent()) {
- continue;
- }
- Block inputTarget = attachedBlock.get();
- Config cfg = BlockStorage.getLocationInfo(input);
- boolean roundrobin = "true".equals(cfg.getString("round-robin"));
-
- ItemStackAndInteger slot = CargoUtils.withdraw(input.getBlock(), inputTarget, Integer.parseInt(cfg.getString("index")));
- if (slot == null) {
- continue;
- }
-
- ItemStack stack = slot.getItem();
- int previousSlot = slot.getInt();
- List outputs = output.get(entry.getValue());
-
- if (outputs != null) {
- List outputList = new LinkedList<>(outputs);
-
- if (roundrobin) {
- roundRobinSort(input, outputList);
- }
-
- for (Location out : outputList) {
- Optional target = getAttachedBlock(out.getBlock());
-
- if (target.isPresent()) {
- stack = CargoUtils.insert(out.getBlock(), target.get(), stack, -1);
-
- if (stack == null) {
- break;
- }
- }
- }
- }
-
- DirtyChestMenu menu = CargoUtils.getChestMenu(inputTarget);
-
- if (menu != null) {
- menu.replaceExistingItem(previousSlot, stack);
- }
- else if (CargoUtils.hasInventory(inputTarget)) {
- BlockState state = inputTarget.getState();
-
- if (state instanceof InventoryHolder) {
- Inventory inv = ((InventoryHolder) state).getInventory();
- inv.setItem(previousSlot, stack);
- }
+ if (attachedBlock.isPresent()) {
+ routeItems(input, attachedBlock.get(), entry.getValue(), output);
}
}
@@ -270,10 +225,59 @@ public class CargoNet extends ChestTerminalNetwork {
}
}
+ private void routeItems(Location inputNode, Block inputTarget, int frequency, Map> outputNodes) {
+ Config cfg = BlockStorage.getLocationInfo(inputNode);
+ boolean roundrobin = "true".equals(cfg.getString("round-robin"));
+
+ ItemStackAndInteger slot = CargoUtils.withdraw(inputNode.getBlock(), inputTarget, Integer.parseInt(cfg.getString("index")));
+ if (slot == null) {
+ return;
+ }
+
+ ItemStack stack = slot.getItem();
+ int previousSlot = slot.getInt();
+ List outputs = outputNodes.get(frequency);
+
+ if (outputs != null) {
+ List outputList = new LinkedList<>(outputs);
+
+ if (roundrobin) {
+ roundRobinSort(inputNode, outputList);
+ }
+
+ for (Location output : outputList) {
+ Optional target = getAttachedBlock(output.getBlock());
+
+ if (target.isPresent()) {
+ stack = CargoUtils.insert(output.getBlock(), target.get(), stack, -1);
+
+ if (stack == null) {
+ break;
+ }
+ }
+ }
+ }
+
+ DirtyChestMenu menu = CargoUtils.getChestMenu(inputTarget);
+
+ if (menu != null) {
+ menu.replaceExistingItem(previousSlot, stack);
+ }
+ else if (CargoUtils.hasInventory(inputTarget)) {
+ BlockState state = inputTarget.getState();
+
+ if (state instanceof InventoryHolder) {
+ Inventory inv = ((InventoryHolder) state).getInventory();
+ inv.setItem(previousSlot, stack);
+ }
+ }
+ }
+
private void roundRobinSort(Location input, List outputs) {
int index = roundRobin.getOrDefault(input, 0);
if (index < outputs.size()) {
+ // Not ideal but actually not bad performance-wise over more elegant alternatives
for (int i = 0; i < index; i++) {
Location temp = outputs.remove(0);
outputs.add(temp);
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/ChestTerminalNetwork.java
index c52e11730..fc0762810 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/ChestTerminalNetwork.java
@@ -27,6 +27,7 @@ import io.github.thebusybiscuit.slimefun4.api.network.Network;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
import me.mrCookieSlime.Slimefun.api.inventory.DirtyChestMenu;
@@ -54,7 +55,7 @@ abstract class ChestTerminalNetwork extends Network {
private final Set itemRequests = new HashSet<>();
protected ChestTerminalNetwork(Location regulator) {
- super(regulator);
+ super(SlimefunPlugin.getNetworkManager(), regulator);
}
protected static Optional getAttachedBlock(Block block) {
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 6ebb39f1b..36b223706 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
@@ -1,6 +1,7 @@
package io.github.thebusybiscuit.slimefun4.core.networks.energy;
import java.util.HashSet;
+import java.util.Optional;
import java.util.Set;
import org.bukkit.Location;
@@ -59,19 +60,17 @@ public class EnergyNet extends Network {
return EnergyNetComponentType.NONE;
}
- public static EnergyNet getNetworkFromLocation(Location l) {
- return SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, EnergyNet.class);
- }
-
public static EnergyNet getNetworkFromLocationOrCreate(Location l) {
- EnergyNet energyNetwork = getNetworkFromLocation(l);
+ Optional cargoNetwork = SlimefunPlugin.getNetworkManager().getNetworkFromLocation(l, EnergyNet.class);
- if (energyNetwork == null) {
- energyNetwork = new EnergyNet(l);
- SlimefunPlugin.getNetworkManager().registerNetwork(energyNetwork);
+ if (cargoNetwork.isPresent()) {
+ return cargoNetwork.get();
+ }
+ else {
+ EnergyNet network = new EnergyNet(l);
+ SlimefunPlugin.getNetworkManager().registerNetwork(network);
+ return network;
}
-
- return energyNetwork;
}
private final Set generators = new HashSet<>();
@@ -79,7 +78,7 @@ public class EnergyNet extends Network {
private final Set consumers = new HashSet<>();
protected EnergyNet(Location l) {
- super(l);
+ super(SlimefunPlugin.getNetworkManager(), l);
}
@Override
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/package-info.java
new file mode 100644
index 000000000..5f4e747af
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * This package provides the core functionality for the {@link io.github.thebusybiscuit.slimefun4.api.network.Network}
+ * class, such as the {@link io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager} and also the subpackages
+ * for actual implementations of the {@link io.github.thebusybiscuit.slimefun4.api.network.Network} class.
+ */
+package io.github.thebusybiscuit.slimefun4.core.networks;
\ No newline at end of file
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
new file mode 100644
index 000000000..384255a91
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/researching/Research.java
@@ -0,0 +1,335 @@
+package io.github.thebusybiscuit.slimefun4.core.researching;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+import org.bukkit.Bukkit;
+import org.bukkit.GameMode;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+
+import io.github.thebusybiscuit.slimefun4.api.events.ResearchUnlockEvent;
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.core.guide.options.SlimefunGuideSettings;
+import io.github.thebusybiscuit.slimefun4.core.services.localization.Language;
+import io.github.thebusybiscuit.slimefun4.implementation.setup.ResearchSetup;
+import io.github.thebusybiscuit.slimefun4.utils.FireworkUtils;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+import me.mrCookieSlime.Slimefun.api.Slimefun;
+
+/**
+ * Represents a research, which is bound to one
+ * {@link SlimefunItem} or more and requires XP levels to unlock said item(s).
+ *
+ * @author TheBusyBiscuit
+ *
+ * @see ResearchSetup
+ * @see ResearchUnlockEvent
+ *
+ */
+public class Research implements Keyed {
+
+ private static final int[] RESEARCH_PROGRESS = { 23, 44, 57, 92 };
+ private static final String PLACEHOLDER_RESEARCH = "%research%";
+
+ private final NamespacedKey key;
+ private final int id;
+ private String name;
+ private boolean enabled = true;
+ private int cost;
+
+ private final List items = new LinkedList<>();
+
+ /**
+ * The constructor for a {@link Research}.
+ *
+ * Create a new research, then bind this research to the Slimefun items you want by calling
+ * {@link #addItems(SlimefunItem...)}. Once you're finished, call {@link #register()}
+ * to register it.
+ *
+ * To speed up, directly setup the research by calling
+ * {@link Slimefun#registerResearch(Research, org.bukkit.inventory.ItemStack...)}.
+ *
+ * @param key
+ * A unique identifier for this {@link Research}
+ * @param id
+ * old way of identifying researches
+ * @param name
+ * The displayed name of this {@link Research}
+ * @param defaultCost
+ * The Cost in XP levels to unlock this {@link Research}
+ *
+ */
+ public Research(NamespacedKey key, int id, String name, int defaultCost) {
+ this.key = key;
+ this.id = id;
+ this.name = name;
+ this.cost = defaultCost;
+ }
+
+ @Override
+ public NamespacedKey getKey() {
+ return key;
+ }
+
+ /**
+ * This method returns whether this {@link Research} is enabled.
+ * {@code false} can mean that this particular {@link Research} was disabled or that
+ * researches alltogether have been disabled.
+ *
+ * @return Whether this {@link Research} is enabled or not
+ */
+ public boolean isEnabled() {
+ return SlimefunPlugin.getRegistry().isResearchingEnabled() && enabled;
+ }
+
+ /**
+ * Gets the ID of this {@link Research}.
+ * This is the old way of identifying Researches, use a {@link NamespacedKey} in the future.
+ *
+ * @deprecated Numeric Ids for Researches are deprecated, use {@link #getKey()} for identification instead.
+ *
+ * @return The ID of this {@link Research}
+ */
+ @Deprecated
+ public int getID() {
+ return id;
+ }
+
+ /**
+ * This method gives you a localized name for this {@link Research}.
+ * The name is automatically taken from the currently selected {@link Language} of
+ * the specified {@link Player}.
+ *
+ * @param p
+ * The {@link Player} to translate this name for.
+ * @return The localized Name of this {@link Research}.
+ */
+ public String getName(Player p) {
+ String localized = SlimefunPlugin.getLocal().getResearchName(p, key);
+ return localized != null ? localized : name;
+ }
+
+ /**
+ * Gets the cost in XP levels to unlock this {@link Research}.
+ *
+ * @return The cost in XP levels for this {@link Research}
+ */
+ public int getCost() {
+ return cost;
+ }
+
+ /**
+ * Sets the cost in XP levels to unlock this {@link Research}.
+ *
+ * @param cost
+ * The cost in XP levels
+ */
+ public void setCost(int cost) {
+ if (cost < 0) {
+ throw new IllegalArgumentException("Research cost must be zero or greater!");
+ }
+
+ this.cost = cost;
+ }
+
+ /**
+ * Bind the specified {@link SlimefunItem SlimefunItems} to this {@link Research}.
+ *
+ * @param items
+ * Instances of {@link SlimefunItem} to bind to this {@link Research}
+ */
+ public void addItems(SlimefunItem... items) {
+ for (SlimefunItem item : items) {
+ if (item != null) {
+ item.setResearch(this);
+ }
+ }
+ }
+
+ /**
+ * Bind the specified ItemStacks to this {@link Research}.
+ *
+ * @param items
+ * Instances of {@link ItemStack} to bind to this {@link Research}
+ *
+ * @return The current instance of {@link Research}
+ */
+ public Research addItems(ItemStack... items) {
+ for (ItemStack item : items) {
+ SlimefunItem sfItem = SlimefunItem.getByItem(item);
+
+ if (sfItem != null) {
+ sfItem.setResearch(this);
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Lists every {@link SlimefunItem} that is bound to this {@link Research}.
+ *
+ * @return The Slimefun items bound to this {@link Research}.
+ */
+ public List getAffectedItems() {
+ return items;
+ }
+
+ /**
+ * Checks if the {@link Player} can unlock this {@link Research}.
+ *
+ * @param p
+ * The {@link Player} to check
+ * @return Whether that {@link Player} can unlock this {@link Research}
+ */
+ public boolean canUnlock(Player p) {
+ if (!isEnabled()) {
+ return true;
+ }
+
+ boolean creativeResearch = p.getGameMode() == GameMode.CREATIVE && SlimefunPlugin.getRegistry().isFreeCreativeResearchingEnabled();
+ return creativeResearch || p.getLevel() >= cost;
+ }
+
+ /**
+ * Unlocks this {@link Research} for the specified {@link Player}.
+ *
+ * @param p
+ * The {@link Player} for which to unlock this {@link Research}
+ * @param instant
+ * Whether to unlock the research instantly
+ */
+ public void unlock(Player p, boolean instant) {
+ if (!instant) {
+ Slimefun.runSync(() -> {
+ p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F);
+ SlimefunPlugin.getLocal().sendMessage(p, "messages.research.progress", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, getName(p)).replace("%progress%", "0%"));
+ }, 10L);
+ }
+
+ PlayerProfile.get(p, profile -> {
+ if (!profile.hasUnlocked(this)) {
+ Slimefun.runSync(() -> {
+ ResearchUnlockEvent event = new ResearchUnlockEvent(p, this);
+ Bukkit.getPluginManager().callEvent(event);
+
+ if (!event.isCancelled()) {
+ if (instant) {
+ finishResearch(p, profile);
+ }
+ else if (SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().add(p.getUniqueId())) {
+ SlimefunPlugin.getLocal().sendMessage(p, "messages.research.start", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, getName(p)));
+ playResearchAnimation(p);
+
+ Slimefun.runSync(() -> {
+ finishResearch(p, profile);
+ SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().remove(p.getUniqueId());
+ }, (RESEARCH_PROGRESS.length + 1) * 20L);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ private void finishResearch(Player p, PlayerProfile profile) {
+ profile.setResearched(this, true);
+ SlimefunPlugin.getLocal().sendMessage(p, "messages.unlocked", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, getName(p)));
+
+ if (SlimefunPlugin.getRegistry().isResearchFireworkEnabled() && SlimefunGuideSettings.hasFireworksEnabled(p)) {
+ FireworkUtils.launchRandom(p, 1);
+ }
+ }
+
+ private void playResearchAnimation(Player p) {
+ for (int i = 1; i < RESEARCH_PROGRESS.length + 1; i++) {
+ int j = i;
+
+ Slimefun.runSync(() -> {
+ p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F);
+ SlimefunPlugin.getLocal().sendMessage(p, "messages.research.progress", true, msg -> msg.replace(PLACEHOLDER_RESEARCH, getName(p)).replace("%progress%", RESEARCH_PROGRESS[j - 1] + "%"));
+ }, i * 20L);
+ }
+ }
+
+ /**
+ * Registers this {@link Research}.
+ */
+ public void register() {
+ SlimefunPlugin.getResearchCfg().setDefaultValue("enable-researching", true);
+
+ String path = key.getNamespace() + '.' + key.getKey();
+
+ if (SlimefunPlugin.getResearchCfg().contains(path + ".enabled") && !SlimefunPlugin.getResearchCfg().getBoolean(path + ".enabled")) {
+ for (SlimefunItem item : new ArrayList<>(items)) {
+ if (item != null) {
+ item.setResearch(null);
+ }
+ }
+
+ enabled = false;
+ return;
+ }
+
+ SlimefunPlugin.getResearchCfg().setDefaultValue(path + ".cost", getCost());
+ SlimefunPlugin.getResearchCfg().setDefaultValue(path + ".enabled", true);
+
+ setCost(SlimefunPlugin.getResearchCfg().getInt(path + ".cost"));
+ enabled = true;
+
+ SlimefunPlugin.getRegistry().getResearches().add(this);
+ }
+
+ /**
+ * Attempts to get a {@link Research} with the given ID.
+ *
+ * @deprecated Numeric Research Ids are fading out, please use {@link #getResearch(NamespacedKey)} instead.
+ *
+ * @param id
+ * ID of the research to get
+ * @return {@link Research} if found, or null
+ */
+ @Deprecated
+ public static Research getByID(int id) {
+ for (Research research : SlimefunPlugin.getRegistry().getResearches()) {
+ if (research.getID() == id) {
+ return research;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attempts to get a {@link Research} with the given {@link NamespacedKey}.
+ *
+ * @param key
+ * the {@link NamespacedKey} of the {@link Research} you are looking for
+ *
+ * @return An {@link Optional} with or without the found {@link Research}
+ */
+ public static Optional getResearch(NamespacedKey key) {
+ if (key == null) {
+ return Optional.empty();
+ }
+
+ for (Research research : SlimefunPlugin.getRegistry().getResearches()) {
+ if (research.getKey().equals(key)) {
+ return Optional.of(research);
+ }
+ }
+
+ return Optional.empty();
+ }
+
+ @Override
+ public String toString() {
+ return "Research (" + getKey() + ')';
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/researching/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/researching/package-info.java
new file mode 100644
index 000000000..ae686d2fd
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/researching/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * This package holds everything connected to the {@link io.github.thebusybiscuit.slimefun4.core.researching.Research}
+ * class.
+ */
+package io.github.thebusybiscuit.slimefun4.core.researching;
\ No newline at end of file
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 41373c822..94dc9f244 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
@@ -84,7 +84,7 @@ public class BlockDataService implements PersistentDataService, Keyed {
* @return Whether the given {@link Material} is considered a Tile Entity
*/
public boolean isTileEntity(Material type) {
- if (!SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
+ if (type == null || !SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
// We can only store data on Tile Entities in 1.14+
// So we will just return false here in that case.
return false;
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 c3a1c97e4..12338756a 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
@@ -1,8 +1,10 @@
package io.github.thebusybiscuit.slimefun4.core.services;
+import java.util.Collection;
+
+import org.apache.commons.lang.Validate;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
-import org.bukkit.plugin.Plugin;
import io.github.thebusybiscuit.cscorelib2.config.Config;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
@@ -21,16 +23,28 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
public class CustomTextureService {
private final Config config;
+
+ private String version = null;
private boolean modified = false;
- public CustomTextureService(Plugin plugin) {
- config = new Config(plugin, "item-models.yml");
-
+ public CustomTextureService(Config config) {
+ this.config = config;
config.getConfiguration().options().header("This file is used to assign items from Slimefun or any of its addons\n" + "the 'CustomModelData' NBT tag. This can be used in conjunction with a custom resource pack\n" + "to give items custom textures.\n0 means there is no data assigned to that item.\n\n" + "There is no official Slimefun resource pack at the moment.");
config.getConfiguration().options().copyHeader(true);
}
- public void register(Iterable items) {
+ /**
+ * This method registers the given {@link SlimefunItem SlimefunItems} to this {@link CustomTextureService}.
+ * If saving is enabled, it will save them to the {@link Config} file.
+ *
+ * @param items
+ * The {@link SlimefunItem SlimefunItems} to register
+ * @param save
+ * Whether to save this file
+ */
+ public void register(Collection items, boolean save) {
+ Validate.notEmpty(items, "items must neither be null or empty.");
+
config.setDefaultValue("SLIMEFUN_GUIDE", 0);
config.setDefaultValue("_UI_BACKGROUND", 0);
@@ -53,11 +67,15 @@ public class CustomTextureService {
}
}
- config.save();
+ version = config.getString("version");
+
+ if (save) {
+ config.save();
+ }
}
public String getVersion() {
- return config.getString("version");
+ return version;
}
public boolean isActive() {
@@ -65,6 +83,7 @@ public class CustomTextureService {
}
public int getModelData(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/LocalizationService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java
index 7df4cdaa9..4d3ede7dd 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/LocalizationService.java
@@ -48,28 +48,34 @@ public class LocalizationService extends SlimefunLocalization implements Persist
this.plugin = plugin;
this.prefix = prefix;
-
- translationsEnabled = SlimefunPlugin.getCfg().getBoolean("options.enable-translations");
languageKey = new NamespacedKey(plugin, LANGUAGE_PATH);
- defaultLanguage = new Language(serverDefaultLanguage, "11b3188fd44902f72602bd7c2141f5a70673a411adb3d81862c69e536166b");
- defaultLanguage.setMessages(getConfig().getConfiguration());
+ if (serverDefaultLanguage != null) {
+ translationsEnabled = SlimefunPlugin.getCfg().getBoolean("options.enable-translations");
+
+ defaultLanguage = new Language(serverDefaultLanguage, "11b3188fd44902f72602bd7c2141f5a70673a411adb3d81862c69e536166b");
+ defaultLanguage.setMessages(getConfig().getConfiguration());
- loadEmbeddedLanguages();
+ loadEmbeddedLanguages();
- String language = getConfig().getString(LANGUAGE_PATH);
- if (language == null) language = serverDefaultLanguage;
+ String language = getConfig().getString(LANGUAGE_PATH);
+ if (language == null) language = serverDefaultLanguage;
- if (hasLanguage(serverDefaultLanguage)) {
- setLanguage(serverDefaultLanguage, !serverDefaultLanguage.equals(language));
+ if (hasLanguage(serverDefaultLanguage)) {
+ setLanguage(serverDefaultLanguage, !serverDefaultLanguage.equals(language));
+ }
+ else {
+ setLanguage("en", false);
+ plugin.getLogger().log(Level.WARNING, "Could not recognize the given language: \"{0}\"", serverDefaultLanguage);
+ }
+
+ Slimefun.getLogger().log(Level.INFO, "Available languages: {0}", String.join(", ", languages.keySet()));
+ save();
}
else {
- setLanguage("en", false);
- plugin.getLogger().log(Level.WARNING, "Could not recognize the given language: \"{0}\"", serverDefaultLanguage);
+ translationsEnabled = false;
+ defaultLanguage = null;
}
-
- Slimefun.getLogger().log(Level.INFO, "Available languages: {0}", String.join(", ", languages.keySet()));
- save();
}
/**
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java
index 4466bf9ab..025b3a119 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MinecraftRecipeService.java
@@ -4,6 +4,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
+import org.apache.commons.lang.Validate;
import org.bukkit.inventory.FurnaceRecipe;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
@@ -60,6 +61,10 @@ public class MinecraftRecipeService {
* @return An {@link Optional} describing the furnace output of the given {@link ItemStack}
*/
public Optional getFurnaceOutput(ItemStack input) {
+ if (input == null) {
+ return Optional.empty();
+ }
+
return snapshot.getRecipeOutput(MinecraftRecipe.FURNACE, input);
}
@@ -75,6 +80,8 @@ public class MinecraftRecipeService {
* @return An Array of {@link RecipeChoice} representing the shape of this {@link Recipe}
*/
public RecipeChoice[] getRecipeShape(Recipe recipe) {
+ Validate.notNull(recipe, "Recipe must not be null!");
+
if (recipe instanceof ShapedRecipe) {
List choices = new LinkedList<>();
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PerWorldSettingsService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PerWorldSettingsService.java
index 636702237..b136a85d0 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PerWorldSettingsService.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PerWorldSettingsService.java
@@ -11,6 +11,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.Server;
@@ -18,6 +19,7 @@ import org.bukkit.World;
import io.github.thebusybiscuit.cscorelib2.collections.OptionalMap;
import io.github.thebusybiscuit.cscorelib2.config.Config;
+import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@@ -32,9 +34,9 @@ public class PerWorldSettingsService {
private final SlimefunPlugin plugin;
- private final OptionalMap> disabledItems = new OptionalMap<>(HashMap::new);
+ private final OptionalMap> disabledItems = new OptionalMap<>(HashMap::new);
private final Map> disabledAddons = new HashMap<>();
- private final Set disabledWorlds = new HashSet<>();
+ private final Set disabledWorlds = new HashSet<>();
public PerWorldSettingsService(SlimefunPlugin plugin) {
this.plugin = plugin;
@@ -66,7 +68,7 @@ public class PerWorldSettingsService {
* The {@link World} to load
*/
public void load(World world) {
- disabledItems.putIfAbsent(world.getName(), loadWorldFromConfig(world.getName()));
+ disabledItems.putIfAbsent(world.getUID(), loadWorldFromConfig(world));
}
/**
@@ -111,9 +113,9 @@ public class PerWorldSettingsService {
* @return Whether the given {@link SlimefunItem} is enabled in that {@link World}
*/
public boolean isEnabled(World world, SlimefunItem item) {
- Set items = disabledItems.computeIfAbsent(world.getName(), this::loadWorldFromConfig);
+ Set items = disabledItems.computeIfAbsent(world.getUID(), id -> loadWorldFromConfig(world));
- if (disabledWorlds.contains(world.getName())) {
+ if (disabledWorlds.contains(world.getUID())) {
return false;
}
@@ -131,7 +133,7 @@ public class PerWorldSettingsService {
* Whether the given {@link SlimefunItem} should be enabled in that world
*/
public void setEnabled(World world, SlimefunItem item, boolean enabled) {
- Set items = disabledItems.computeIfAbsent(world.getName(), this::loadWorldFromConfig);
+ Set items = disabledItems.computeIfAbsent(world.getUID(), id -> loadWorldFromConfig(world));
if (enabled) {
items.remove(item.getID());
@@ -141,6 +143,25 @@ public class PerWorldSettingsService {
}
}
+ /**
+ * This method enables or disables the given {@link World}.
+ *
+ * @param world
+ * The {@link World} to enable or disable
+ * @param enabled
+ * Whether this {@link World} should be enabled or not
+ */
+ public void setEnabled(World world, boolean enabled) {
+ load(world);
+
+ if (enabled) {
+ disabledWorlds.remove(world.getUID());
+ }
+ else {
+ disabledWorlds.add(world.getUID());
+ }
+ }
+
/**
* This checks whether the given {@link World} is enabled or not.
*
@@ -151,7 +172,8 @@ public class PerWorldSettingsService {
*/
public boolean isWorldEnabled(World world) {
load(world);
- return !disabledWorlds.contains(world.getName());
+
+ return !disabledWorlds.contains(world.getUID());
}
/**
@@ -177,7 +199,7 @@ public class PerWorldSettingsService {
* The {@link World} to save
*/
public void save(World world) {
- Set items = disabledItems.computeIfAbsent(world.getName(), this::loadWorldFromConfig);
+ Set items = disabledItems.computeIfAbsent(world.getUID(), id -> loadWorldFromConfig(world));
Config config = new Config(plugin, "world-settings/" + world + ".yml");
@@ -191,8 +213,9 @@ public class PerWorldSettingsService {
config.save();
}
- private Set loadWorldFromConfig(String name) {
- Optional> optional = disabledItems.get(name);
+ private Set loadWorldFromConfig(World world) {
+ String name = world.getName();
+ Optional> optional = disabledItems.get(world.getUID());
if (optional.isPresent()) {
return optional.get();
@@ -225,10 +248,12 @@ public class PerWorldSettingsService {
}
}
- config.save();
+ if (SlimefunPlugin.getMinecraftVersion() != MinecraftVersion.UNIT_TEST) {
+ config.save();
+ }
}
else {
- disabledWorlds.add(name);
+ disabledWorlds.add(world.getUID());
}
return items;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PermissionsService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PermissionsService.java
index 879c917e5..8944f712b 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PermissionsService.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/PermissionsService.java
@@ -33,7 +33,7 @@ public class PermissionsService {
config.getConfiguration().options().copyHeader(true);
}
- public void register(Iterable items) {
+ public void register(Iterable items, boolean save) {
for (SlimefunItem item : items) {
if (item != null && item.getID() != null && !migrate(item)) {
config.setDefaultValue(item.getID() + ".permission", "none");
@@ -42,7 +42,9 @@ public class PermissionsService {
}
}
- config.save();
+ if (save) {
+ config.save();
+ }
}
// Temporary migration method for the old system
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 60bfb6947..545672f93 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/UpdaterService.java
@@ -5,6 +5,7 @@ import java.util.logging.Level;
import org.bukkit.plugin.Plugin;
+import io.github.thebusybiscuit.cscorelib2.config.Config;
import io.github.thebusybiscuit.cscorelib2.updater.GitHubBuildsUpdater;
import io.github.thebusybiscuit.cscorelib2.updater.Updater;
import io.github.thebusybiscuit.slimefun4.api.SlimefunBranch;
@@ -30,32 +31,47 @@ public class UpdaterService {
*
* @param plugin
* The instance of Slimefun
+ * @param version
+ * The current version of Slimefun
* @param file
* The {@link File} of this {@link Plugin}
*/
- public UpdaterService(SlimefunPlugin plugin, File file) {
+ public UpdaterService(SlimefunPlugin plugin, String version, File file) {
this.plugin = plugin;
- String version = plugin.getDescription().getVersion();
+ Updater autoUpdater = null;
if (version.contains("UNOFFICIAL")) {
// This Server is using a modified build that is not a public release.
- updater = null;
branch = SlimefunBranch.UNOFFICIAL;
}
else if (version.startsWith("DEV - ")) {
// If we are using a development build, we want to switch to our custom
- updater = new GitHubBuildsUpdater(plugin, file, "TheBusyBiscuit/Slimefun4/master");
+ try {
+ autoUpdater = new GitHubBuildsUpdater(plugin, file, "TheBusyBiscuit/Slimefun4/master");
+
+ }
+ catch (Exception x) {
+ plugin.getLogger().log(Level.SEVERE, "Failed to create AutoUpdater", x);
+ }
+
branch = SlimefunBranch.DEVELOPMENT;
}
else if (version.startsWith("RC - ")) {
// If we are using a "stable" build, we want to switch to our custom
- updater = new GitHubBuildsUpdater(plugin, file, "TheBusyBiscuit/Slimefun4/stable", "RC - ");
+ try {
+ autoUpdater = new GitHubBuildsUpdater(plugin, file, "TheBusyBiscuit/Slimefun4/stable", "RC - ");
+ }
+ catch (Exception x) {
+ plugin.getLogger().log(Level.SEVERE, "Failed to create AutoUpdater", x);
+ }
+
branch = SlimefunBranch.STABLE;
}
else {
- updater = null;
branch = SlimefunBranch.UNKNOWN;
}
+
+ this.updater = autoUpdater;
}
/**
@@ -86,6 +102,17 @@ public class UpdaterService {
}
}
+ /**
+ * This returns whether the {@link Updater} is enabled or not.
+ * This includes the {@link Config} setting but also whether or not we are running an
+ * official or unofficial build.
+ *
+ * @return Whether the {@link Updater} is enabled
+ */
+ public boolean isEnabled() {
+ return SlimefunPlugin.getCfg().getBoolean("options.auto-update") && updater != null;
+ }
+
/**
* This method is called when the {@link UpdaterService} was disabled.
*/
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 cef0c8759..b8822be33 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
@@ -35,6 +35,12 @@ public class GitHubService {
private int stars = 0;
private LocalDateTime lastUpdate = LocalDateTime.now();
+ /**
+ * This creates a new {@link GitHubService} for the given repository.
+ *
+ * @param repository
+ * The repository to create this {@link GitHubService} for
+ */
public GitHubService(String repository) {
this.repository = repository;
@@ -70,10 +76,10 @@ public class GitHubService {
connectors.add(new ContributionsConnector(this, "code2", 2, repository, "developer"));
// TheBusyBiscuit/Slimefun4-Wiki
- connectors.add(new ContributionsConnector(this, "wiki", 1, "TheBusyBiscuit/Slimefun4-wiki", "wiki"));
+ connectors.add(new ContributionsConnector(this, "wiki", 1, "Slimefun/Slimefun-wiki", "wiki"));
// TheBusyBiscuit/Slimefun4-Resourcepack
- connectors.add(new ContributionsConnector(this, "resourcepack", 1, "TheBusyBiscuit/Slimefun4-Resourcepack", "resourcepack"));
+ connectors.add(new ContributionsConnector(this, "resourcepack", 1, "Slimefun/Resourcepack", "resourcepack"));
// Issues and Pull Requests
connectors.add(new GitHubIssuesTracker(this, repository, (issues, pullRequests) -> {
@@ -103,35 +109,74 @@ public class GitHubService {
});
}
- public Set getConnectors() {
+ protected Set getConnectors() {
return connectors;
}
+ protected boolean isLoggingEnabled() {
+ return logging;
+ }
+
+ /**
+ * This returns the {@link Contributor Contributors} to this project.
+ *
+ * @return A {@link ConcurrentMap} containing all {@link Contributor Contributors}
+ */
public ConcurrentMap getContributors() {
return contributors;
}
+ /**
+ * This returns the amount of forks of our repository
+ *
+ * @return The amount of forks
+ */
public int getForks() {
return forks;
}
+ /**
+ * This method returns the amount of stargazers of the repository.
+ *
+ * @return The amount of people who starred the repository
+ */
public int getStars() {
return stars;
}
- public int getIssues() {
+ /**
+ * This returns the amount of open Issues on our repository.
+ *
+ * @return The amount of open issues
+ */
+ public int getOpenissues() {
return issues;
}
- public int getPullRequests() {
+ /**
+ * Returns the id of Slimefun's GitHub Repository. (e.g. "TheBusyBiscuit/Slimefun4").
+ *
+ * @return The id of our GitHub Repository
+ */
+ public String getRepository() {
+ return repository;
+ }
+
+ /**
+ * This method returns the amount of pending pull requests.
+ *
+ * @return The amount of pending pull requests
+ */
+ public int getPendingPullRequests() {
return pullRequests;
}
+ /**
+ * This returns the date and time of the last commit to this repository.
+ *
+ * @return A {@link LocalDateTime} object representing the date and time of the latest commit
+ */
public LocalDateTime getLastUpdate() {
return lastUpdate;
}
-
- public boolean isLoggingEnabled() {
- return logging;
- }
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java
index f238c46d6..ad6e180b2 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/localization/SlimefunLocalization.java
@@ -1,5 +1,6 @@
package io.github.thebusybiscuit.slimefun4.core.services.localization;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.UnaryOperator;
@@ -85,11 +86,13 @@ public abstract class SlimefunLocalization extends Localization implements Keyed
public String getMessage(Player p, String key) {
Language language = getLanguage(p);
+ if (language == null) return "NO LANGUAGE FOUND";
return language.getMessages().getString(key);
}
public List getMessages(Player p, String key) {
Language language = getLanguage(p);
+ if (language == null) return Arrays.asList("NO LANGUAGE FOUND");
return language.getMessages().getStringList(key);
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/metrics/AutoUpdaterChart.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/metrics/AutoUpdaterChart.java
index ff6d137c5..c05eb9781 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/metrics/AutoUpdaterChart.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/metrics/AutoUpdaterChart.java
@@ -8,7 +8,7 @@ class AutoUpdaterChart extends SimplePie {
AutoUpdaterChart() {
super("auto_updates", () -> {
- boolean enabled = SlimefunPlugin.getCfg().getBoolean("options.auto-update");
+ boolean enabled = SlimefunPlugin.getUpdater().isEnabled();
return enabled ? "enabled" : "disabled";
});
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/PlaceholderAPIHook.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/PlaceholderAPIHook.java
index 9a348dc86..28e56331e 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/PlaceholderAPIHook.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/plugins/PlaceholderAPIHook.java
@@ -1,5 +1,6 @@
package io.github.thebusybiscuit.slimefun4.core.services.plugins;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
@@ -7,9 +8,9 @@ import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
-import me.mrCookieSlime.Slimefun.Objects.Research;
class PlaceholderAPIHook extends PlaceholderExpansion {
@@ -40,27 +41,43 @@ class PlaceholderAPIHook extends PlaceholderExpansion {
@Override
public String onRequest(OfflinePlayer p, String params) {
- if (params.equals("researches_total_xp_levels_spent")) {
- Stream stream = PlayerProfile.get(p).getResearches().stream();
- return String.valueOf(stream.mapToInt(Research::getCost).sum());
+ if (params.equals("researches_total_xp_levels_spent") && PlayerProfile.request(p)) {
+ Optional profile = PlayerProfile.find(p);
+
+ if (profile.isPresent()) {
+ Stream stream = profile.get().getResearches().stream();
+ return String.valueOf(stream.mapToInt(Research::getCost).sum());
+ }
}
- if (params.equals("researches_total_researches_unlocked")) {
- Set set = PlayerProfile.get(p).getResearches();
- return String.valueOf(set.size());
+ if (params.equals("researches_total_researches_unlocked") && PlayerProfile.request(p)) {
+ Optional profile = PlayerProfile.find(p);
+
+ if (profile.isPresent()) {
+ Set set = profile.get().getResearches();
+ return String.valueOf(set.size());
+ }
}
if (params.equals("researches_total_researches")) {
return String.valueOf(SlimefunPlugin.getRegistry().getResearches().size());
}
- if (params.equals("researches_percentage_researches_unlocked")) {
- Set set = PlayerProfile.get(p).getResearches();
- return String.valueOf(Math.round(((set.size() * 100.0F) / SlimefunPlugin.getRegistry().getResearches().size()) * 100.0F) / 100.0F);
+ if (params.equals("researches_percentage_researches_unlocked") && PlayerProfile.request(p)) {
+ Optional profile = PlayerProfile.find(p);
+
+ if (profile.isPresent()) {
+ Set set = profile.get().getResearches();
+ return String.valueOf(Math.round(((set.size() * 100.0F) / SlimefunPlugin.getRegistry().getResearches().size()) * 100.0F) / 100.0F);
+ }
}
- if (params.equals("researches_title")) {
- return PlayerProfile.get(p).getTitle();
+ if (params.equals("researches_title") && PlayerProfile.request(p)) {
+ Optional profile = PlayerProfile.find(p);
+
+ if (profile.isPresent()) {
+ return profile.get().getTitle();
+ }
}
if (params.equals("gps_complexity")) {
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 6c22451da..66788027e 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
@@ -21,15 +21,15 @@ import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
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.categories.LockedCategory;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideImplementation;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideLayout;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.Category;
-import me.mrCookieSlime.Slimefun.Objects.LockedCategory;
-import me.mrCookieSlime.Slimefun.Objects.Research;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.Slimefun;
@@ -47,7 +47,7 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation {
return new CustomItem(new ItemStack(Material.ENCHANTED_BOOK), "&aSlimefun Guide &7(Book GUI)", "", "&eRight Click &8\u21E8 &7Browse Items", "&eShift + Right Click &8\u21E8 &7Open Settings / Credits");
}
- private void openBook(Player p, List lines, boolean backButton) {
+ private void openBook(Player p, PlayerProfile profile, List lines, boolean backButton) {
CustomBookInterface book = new CustomBookInterface(SlimefunPlugin.instance);
book.setTitle(SlimefunPlugin.getLocal().getMessage(p, "guide.title.main"));
@@ -56,10 +56,10 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation {
ChatComponent header = new ChatComponent(ChatColors.color("&b&l- " + SlimefunPlugin.getLocal().getMessage(p, "guide.title.main") + " -\n\n"));
header.setHoverEvent(new HoverEvent(ChestMenuUtils.getSearchButton(p)));
- header.setClickEvent(new ClickEvent(guideSearch, player -> PlayerProfile.get(player, profile -> Slimefun.runSync(() -> {
+ header.setClickEvent(new ClickEvent(guideSearch, player -> Slimefun.runSync(() -> {
SlimefunPlugin.getLocal().sendMessage(player, "guide.search.message");
ChatInput.waitForPlayer(SlimefunPlugin.instance, player, msg -> SlimefunGuide.openSearch(profile, msg, true, true));
- }, 1))));
+ }, 1)));
page.append(header);
@@ -72,7 +72,7 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation {
if (backButton) {
ChatComponent button = new ChatComponent(ChatColor.DARK_BLUE + "\u21E6 " + SlimefunPlugin.getLocal().getMessage(p, "guide.back.title"));
button.setHoverEvent(new HoverEvent(ChatColor.DARK_BLUE + "\u21E6 " + SlimefunPlugin.getLocal().getMessage(p, "guide.back.title"), "", ChatColor.GRAY + SlimefunPlugin.getLocal().getMessage(p, "guide.back.guide")));
- button.setClickEvent(new ClickEvent(new NamespacedKey(SlimefunPlugin.instance, "slimefun_guide"), pl -> openMainMenu(PlayerProfile.get(pl), 1)));
+ button.setClickEvent(new ClickEvent(new NamespacedKey(SlimefunPlugin.instance, "slimefun_guide"), pl -> openMainMenu(profile, 1)));
page.append(button);
}
@@ -132,7 +132,7 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation {
}
}
- openBook(p, lines, false);
+ openBook(p, profile, lines, false);
}
@Override
@@ -206,7 +206,7 @@ public class BookSlimefunGuide implements SlimefunGuideImplementation {
}
}
- openBook(p, lines, true);
+ openBook(p, profile, lines, true);
}
else {
p.sendMessage(ChatColor.RED + "That Category is too big to open :/");
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/ChestSlimefunGuide.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/ChestSlimefunGuide.java
index a9f28a350..6e6ca9518 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/ChestSlimefunGuide.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/guide/ChestSlimefunGuide.java
@@ -28,11 +28,13 @@ import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.MultiBlock;
import io.github.thebusybiscuit.slimefun4.core.attributes.RecipeDisplayItem;
import io.github.thebusybiscuit.slimefun4.core.categories.FlexCategory;
+import io.github.thebusybiscuit.slimefun4.core.categories.LockedCategory;
import io.github.thebusybiscuit.slimefun4.core.guide.GuideHistory;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideImplementation;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideLayout;
import io.github.thebusybiscuit.slimefun4.core.guide.options.SlimefunGuideSettings;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils;
import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils;
import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu;
@@ -40,8 +42,6 @@ import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu.MenuClickHan
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
-import me.mrCookieSlime.Slimefun.Objects.LockedCategory;
-import me.mrCookieSlime.Slimefun.Objects.Research;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.multiblocks.MultiBlockMachine;
import me.mrCookieSlime.Slimefun.api.Slimefun;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/RestoredBackpack.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/RestoredBackpack.java
new file mode 100644
index 000000000..02738079e
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/RestoredBackpack.java
@@ -0,0 +1,34 @@
+package io.github.thebusybiscuit.slimefun4.implementation.items.backpacks;
+
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack;
+import me.mrCookieSlime.Slimefun.Lists.RecipeType;
+import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
+import me.mrCookieSlime.Slimefun.Objects.Category;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+
+/**
+ * This class represents a {@link SlimefunBackpack} that has been restored via /sf backpack for retrieving items if the
+ * original has been lost.
+ * This backpack cannot be crafted nor crafted into other items. Its purpose is exclusively that of restoring
+ * the lost inventory and shouldn't be used as a backpack replacement.
+ * Right-Clicking will open the {@link Inventory} of the restored Backpack.
+ *
+ * @author Sfiguz7
+ *
+ * @see PlayerBackpack
+ */
+public class RestoredBackpack extends SlimefunBackpack {
+
+ /**
+ * This will create a new {@link SlimefunBackpack} with the given arguments.
+ *
+ * @param category
+ * the category to bind this {@link SlimefunBackpack} to
+ */
+ public RestoredBackpack(Category category) {
+ super(54, category, SlimefunItems.RESTORED_BACKPACK, RecipeType.NULL, new ItemStack[9]);
+
+ this.hidden = true;
+ }
+}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/SlimefunBackpack.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SlimefunBackpack.java
similarity index 99%
rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/SlimefunBackpack.java
rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SlimefunBackpack.java
index c7ac3ba88..b27d8972d 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/SlimefunBackpack.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SlimefunBackpack.java
@@ -1,4 +1,4 @@
-package io.github.thebusybiscuit.slimefun4.implementation.items.tools;
+package io.github.thebusybiscuit.slimefun4.implementation.items.backpacks;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundBackpack.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SoulboundBackpack.java
similarity index 66%
rename from src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundBackpack.java
rename to src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SoulboundBackpack.java
index 6abf6e258..362c146ee 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/SoulboundBackpack.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/SoulboundBackpack.java
@@ -1,17 +1,22 @@
-package io.github.thebusybiscuit.slimefun4.implementation.items.magical;
+package io.github.thebusybiscuit.slimefun4.implementation.items.backpacks;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound;
-import io.github.thebusybiscuit.slimefun4.implementation.items.tools.SlimefunBackpack;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
+/**
+ * This implementation of {@link SlimefunBackpack} is also {@link Soulbound}.
+ *
+ * @author TheBusyBiscuit
+ *
+ */
public class SoulboundBackpack extends SlimefunBackpack implements Soulbound {
- public SoulboundBackpack(int size, Category category, SlimefunItemStack item, ItemStack[] recipe) {
- super(size, category, item, RecipeType.MAGIC_WORKBENCH, recipe);
+ public SoulboundBackpack(int size, Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
+ super(size, category, item, recipeType, recipe);
}
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/package-info.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/package-info.java
new file mode 100644
index 000000000..ccfed4a9a
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/backpacks/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * This package holds classes related to
+ * {@link io.github.thebusybiscuit.slimefun4.implementation.items.tools.SlimefunBackpack}.
+ */
+package io.github.thebusybiscuit.slimefun4.implementation.items.backpacks;
\ No newline at end of file
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/EnhancedFurnace.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/EnhancedFurnace.java
index 625e90f99..88eaa1987 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/EnhancedFurnace.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/EnhancedFurnace.java
@@ -72,7 +72,7 @@ public class EnhancedFurnace extends SimpleSlimefunItem {
if (furnace.getCookTime() > 0) {
int cookTime = furnace.getCookTime() + getSpeed() * 10;
- furnace.setCookTime((short) Math.min(cookTime, furnace.getCookTimeTotal()));
+ furnace.setCookTime((short) Math.min(cookTime, furnace.getCookTimeTotal() - 1));
furnace.update(true, false);
}
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/UnplaceableBlock.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/UnplaceableBlock.java
new file mode 100644
index 000000000..e1ed09d93
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/UnplaceableBlock.java
@@ -0,0 +1,24 @@
+package io.github.thebusybiscuit.slimefun4.implementation.items.blocks;
+
+import org.bukkit.inventory.ItemStack;
+
+import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent;
+import me.mrCookieSlime.Slimefun.Lists.RecipeType;
+import me.mrCookieSlime.Slimefun.Objects.Category;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SimpleSlimefunItem;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.interfaces.NotPlaceable;
+import me.mrCookieSlime.Slimefun.Objects.handlers.ItemUseHandler;
+import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
+
+public class UnplaceableBlock extends SimpleSlimefunItem implements NotPlaceable {
+
+ public UnplaceableBlock(Category category, SlimefunItemStack item, RecipeType recipeType, ItemStack[] recipe) {
+ super(category, item, recipeType, recipe);
+ }
+
+ @Override
+ public ItemUseHandler getItemHandler() {
+ return PlayerRightClickEvent::cancel;
+ }
+
+}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoBreeder.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoBreeder.java
index e4ced6a19..0a6427a4d 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoBreeder.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/AutoBreeder.java
@@ -97,10 +97,12 @@ public class AutoBreeder extends SlimefunItem implements InventoryBlock, EnergyN
protected void tick(Block b) {
BlockMenu inv = BlockStorage.getInventory(b);
- for (Entity n : b.getWorld().getNearbyEntities(b.getLocation(), 4.0, 2.0, 4.0, n -> n instanceof Animals && n.isValid() && ((Animals) n).isAdult() && !((Animals) n).isLoveMode())) {
+ for (Entity n : b.getWorld().getNearbyEntities(b.getLocation(), 4.0, 2.0, 4.0, this::canBreed)) {
for (int slot : getInputSlots()) {
if (SlimefunUtils.isItemSimilar(inv.getItemInSlot(slot), SlimefunItems.ORGANIC_FOOD, false)) {
- if (ChargableBlock.getCharge(b) < ENERGY_CONSUMPTION) return;
+ if (ChargableBlock.getCharge(b) < ENERGY_CONSUMPTION) {
+ return;
+ }
ChargableBlock.addCharge(b, -ENERGY_CONSUMPTION);
inv.consumeItem(slot);
@@ -113,4 +115,14 @@ public class AutoBreeder extends SlimefunItem implements InventoryBlock, EnergyN
}
}
+ private boolean canBreed(Entity n) {
+ if (n.isValid() && n instanceof Animals) {
+ Animals animal = (Animals) n;
+
+ return animal.isAdult() && !animal.isLoveMode();
+ }
+
+ return false;
+ }
+
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/food/Cooler.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/food/Cooler.java
index 9b79c7cc8..298c5c136 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/food/Cooler.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/food/Cooler.java
@@ -3,7 +3,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.food;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
-import io.github.thebusybiscuit.slimefun4.implementation.items.tools.SlimefunBackpack;
+import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.CoolerListener;
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/KnowledgeTome.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/KnowledgeTome.java
index 8619b7812..174ce5907 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/KnowledgeTome.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/KnowledgeTome.java
@@ -13,10 +13,10 @@ import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Objects.Category;
-import me.mrCookieSlime.Slimefun.Objects.Research;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SimpleSlimefunItem;
import me.mrCookieSlime.Slimefun.Objects.handlers.ItemUseHandler;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
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/SoulboundRune.java
index eb7f6c9da..5db4f782c 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/SoulboundRune.java
@@ -42,8 +42,9 @@ public class SoulboundRune extends SimpleSlimefunItem {
@Override
public ItemDropHandler getItemHandler() {
- return (e, p, i) -> {
- ItemStack item = i.getItemStack();
+ return (e, p, droppedItem) -> {
+ ItemStack item = droppedItem.getItemStack();
+
if (isItem(item)) {
if (!Slimefun.hasUnlocked(p, SlimefunItems.RUNE_SOULBOUND, true)) {
@@ -52,42 +53,39 @@ public class SoulboundRune extends SimpleSlimefunItem {
Slimefun.runSync(() -> {
// Being sure the entity is still valid and not picked up or whatsoever.
- if (!i.isValid()) return;
+ if (!droppedItem.isValid()) {
+ return;
+ }
- Location l = i.getLocation();
+ Location l = droppedItem.getLocation();
Collection entites = l.getWorld().getNearbyEntities(l, 1.5, 1.5, 1.5, this::findCompatibleItem);
- if (entites.isEmpty()) return;
+ if (entites.isEmpty()) {
+ return;
+ }
Entity entity = entites.stream().findFirst().get();
- ItemStack ench = ((Item) entity).getItemStack();
- Item ent = (Item) entity;
+ ItemStack target = ((Item) entity).getItemStack();
+ Item targetItem = (Item) entity;
- if (ench.getAmount() == 1) {
+ SlimefunUtils.setSoulbound(target, true);
+
+ if (target.getAmount() == 1) {
e.setCancelled(true);
- ItemMeta enchMeta = ench.getItemMeta();
- List lore = enchMeta.hasLore() ? enchMeta.getLore() : new ArrayList<>();
-
// This lightning is just an effect, it deals no damage.
l.getWorld().strikeLightningEffect(l);
Slimefun.runSync(() -> {
-
// Being sure entities are still valid and not picked up or whatsoever.
- if (i.isValid() && ent.isValid()) {
+ if (droppedItem.isValid() && targetItem.isValid() && target.getAmount() == 1) {
l.getWorld().createExplosion(l, 0.0F);
l.getWorld().playSound(l, Sound.ENTITY_GENERIC_EXPLODE, 0.3F, 1F);
- lore.add(ChatColor.GRAY + "Soulbound");
-
- enchMeta.setLore(lore);
- ench.setItemMeta(enchMeta);
-
- ent.remove();
- i.remove();
- l.getWorld().dropItemNaturally(l, ench);
+ targetItem.remove();
+ droppedItem.remove();
+ l.getWorld().dropItemNaturally(l, target);
SlimefunPlugin.getLocal().sendMessage(p, "messages.soulbound-rune.success", true);
}
@@ -104,6 +102,22 @@ public class SoulboundRune extends SimpleSlimefunItem {
};
}
+ /**
+ * This method applies the {@link Soulbound} effect onto a given {@link ItemStack}.
+ *
+ * @param item
+ * The {@link ItemStack} to apply this effect to
+ */
+ public void apply(ItemStack item) {
+ // Should rather use PersistentData here
+ ItemMeta meta = item.getItemMeta();
+ List lore = meta.hasLore() ? meta.getLore() : new ArrayList<>();
+ lore.add(ChatColor.GRAY + "Soulbound");
+
+ meta.setLore(lore);
+ item.setItemMeta(meta);
+ }
+
private boolean findCompatibleItem(Entity n) {
if (n instanceof Item) {
Item item = (Item) n;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/EnderTalisman.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/EnderTalisman.java
index 529b15180..9b58fb0e6 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/EnderTalisman.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/EnderTalisman.java
@@ -5,9 +5,9 @@ import org.bukkit.block.EnderChest;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.core.categories.LockedCategory;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
-import me.mrCookieSlime.Slimefun.Objects.LockedCategory;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
/**
@@ -21,13 +21,8 @@ class EnderTalisman extends Talisman {
private static final LockedCategory ENDER_TALISMANS_CATEGORY = new LockedCategory(new NamespacedKey(SlimefunPlugin.instance, "ender_talismans"), new CustomItem(SlimefunItems.ENDER_TALISMAN, "&7Talismans - &aTier II"), 3, Talisman.TALISMANS_CATEGORY.getKey());
- public EnderTalisman(Talisman parent) {
- super(ENDER_TALISMANS_CATEGORY, parent.upgrade(), new ItemStack[] { SlimefunItems.ENDER_LUMP_3, null, SlimefunItems.ENDER_LUMP_3, null, parent.getItem(), null, SlimefunItems.ENDER_LUMP_3, null, SlimefunItems.ENDER_LUMP_3 }, parent.isConsumable(), parent.isEventCancelled(), parent.getSuffix(), parent.getChance(), parent.getEffects());
- }
-
- @Override
- public SlimefunItemStack upgrade() {
- throw new UnsupportedOperationException();
+ public EnderTalisman(Talisman parent, SlimefunItemStack item) {
+ super(ENDER_TALISMANS_CATEGORY, item, new ItemStack[] { SlimefunItems.ENDER_LUMP_3, null, SlimefunItems.ENDER_LUMP_3, null, parent.getItem(), null, SlimefunItems.ENDER_LUMP_3, null, SlimefunItems.ENDER_LUMP_3 }, parent.isConsumable(), parent.isEventCancelled(), parent.getMessageSuffix(), parent.getChance(), parent.getEffects());
}
@Override
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/Talisman.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/Talisman.java
index 671afbd3d..ca82fdd4d 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/Talisman.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/talismans/Talisman.java
@@ -2,6 +2,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.magical.talisman
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import org.bukkit.ChatColor;
@@ -19,11 +20,11 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
import me.mrCookieSlime.Slimefun.Objects.Category;
-import me.mrCookieSlime.Slimefun.Objects.Research;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
@@ -32,6 +33,8 @@ public class Talisman extends SlimefunItem {
protected static final Category TALISMANS_CATEGORY = new Category(new NamespacedKey(SlimefunPlugin.instance, "talismans"), new CustomItem(SlimefunItems.TALISMAN, "&7Talismans - &aTier I"), 2);
+ private final SlimefunItemStack enderTalisman;
+
protected final String suffix;
protected final boolean consumable;
protected final boolean cancel;
@@ -58,43 +61,51 @@ public class Talisman extends SlimefunItem {
this.suffix = messageSuffix;
this.effects = effects;
this.chance = chance;
- }
- public String getSuffix() {
- return suffix;
+ if (!(this instanceof EnderTalisman)) {
+ String name = "&5Ender " + ChatColor.stripColor(getItem().getItemMeta().getDisplayName());
+ List lore = new ArrayList<>();
+ lore.add("&7&oEnder Infused");
+ lore.add("");
+
+ for (String line : getItem().getItemMeta().getLore()) {
+ lore.add(line);
+ }
+
+ enderTalisman = new SlimefunItemStack("ENDER_" + getID(), getItem().getType(), name, lore.toArray(new String[0]));
+ }
+ else {
+ enderTalisman = null;
+ }
}
public boolean isConsumable() {
return consumable;
}
- public boolean isEventCancelled() {
- return cancel;
+ public int getChance() {
+ return chance;
}
public PotionEffect[] getEffects() {
return effects;
}
- public int getChance() {
- return chance;
+ protected String getMessageSuffix() {
+ return suffix;
}
- public SlimefunItemStack upgrade() {
- List lore = new ArrayList<>();
- lore.add("&7&oEnder Infused");
- lore.add("");
+ protected boolean isEventCancelled() {
+ return cancel;
+ }
- for (String line : getItem().getItemMeta().getLore()) {
- lore.add(line);
- }
-
- return new SlimefunItemStack("ENDER_" + getID(), getItem().getType(), "&5Ender " + ChatColor.stripColor(getItem().getItemMeta().getDisplayName()), lore.toArray(new String[lore.size()]));
+ private SlimefunItemStack getEnderVariant() {
+ return enderTalisman;
}
@Override
public void postRegister() {
- EnderTalisman talisman = new EnderTalisman(this);
+ EnderTalisman talisman = new EnderTalisman(this, getEnderVariant());
talisman.register(addon);
}
@@ -105,16 +116,16 @@ public class Talisman extends SlimefunItem {
}
protected void createEnderTalisman() {
- EnderTalisman talisman = (EnderTalisman) SlimefunItem.getByItem(upgrade());
- Research research = Research.getByID(112);
+ EnderTalisman talisman = (EnderTalisman) SlimefunItem.getByItem(getEnderVariant());
+ Optional research = Research.getResearch(new NamespacedKey(SlimefunPlugin.instance, "ender_talismans"));
- if (talisman != null && research != null) {
- talisman.setResearch(research);
+ if (talisman != null && research.isPresent()) {
+ talisman.setResearch(research.get());
}
}
private static boolean hasMessage(Talisman talisman) {
- return !("").equalsIgnoreCase(talisman.getSuffix());
+ return !("").equalsIgnoreCase(talisman.getMessageSuffix());
}
public static boolean checkFor(Event e, SlimefunItemStack stack) {
@@ -137,25 +148,37 @@ public class Talisman extends SlimefunItem {
return false;
}
- if (p.getInventory().containsAtLeast(talisman.getItem(), 1)) {
- if (Slimefun.hasUnlocked(p, talisman.getItem(), true)) {
- activateTalisman(e, p, p.getInventory(), talisman, talisman.getItem());
+ ItemStack talismanItem = talisman.getItem();
+
+ if (p.getInventory().containsAtLeast(talismanItem, 1)) {
+ if (Slimefun.hasUnlocked(p, talismanItem, true)) {
+ activateTalisman(e, p, p.getInventory(), talisman, talismanItem);
return true;
}
- else return false;
- }
- else if (p.getEnderChest().containsAtLeast(talisman.upgrade(), 1)) {
- if (Slimefun.hasUnlocked(p, talisman.upgrade(), true)) {
- activateTalisman(e, p, p.getEnderChest(), talisman, talisman.upgrade());
- return true;
+ else {
+ return false;
+ }
+ }
+ else {
+ ItemStack enderTalisman = talisman.getEnderVariant();
+
+ if (p.getEnderChest().containsAtLeast(enderTalisman, 1)) {
+ if (Slimefun.hasUnlocked(p, enderTalisman, true)) {
+ activateTalisman(e, p, p.getEnderChest(), talisman, enderTalisman);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ return false;
}
- else return false;
}
- else return false;
}
- private static void activateTalisman(Event e, Player p, Inventory inv, Talisman talisman, ItemStack talismantype) {
- consumeItem(inv, talisman, talismantype);
+ private static void activateTalisman(Event e, Player p, Inventory inv, Talisman talisman, ItemStack talismanItem) {
+ consumeItem(inv, talisman, talismanItem);
applyTalismanEffects(p, talisman);
cancelEvent(e, talisman);
sendMessage(p, talisman);
@@ -176,22 +199,32 @@ public class Talisman extends SlimefunItem {
private static void sendMessage(Player p, Talisman talisman) {
if (hasMessage(talisman)) {
- SlimefunPlugin.getLocal().sendMessage(p, "messages.talisman." + talisman.getSuffix(), true);
+ SlimefunPlugin.getLocal().sendMessage(p, "messages.talisman." + talisman.getMessageSuffix(), true);
}
}
- private static void consumeItem(Inventory inv, Talisman talisman, ItemStack talismantype) {
+ private static void consumeItem(Inventory inv, Talisman talisman, ItemStack talismanItem) {
if (talisman.isConsumable()) {
- inv.removeItem(talismantype);
+ inv.removeItem(talismanItem);
}
}
private static Player getPlayerByEventType(Event e) {
- if (e instanceof EntityDeathEvent) return ((EntityDeathEvent) e).getEntity().getKiller();
- else if (e instanceof BlockBreakEvent) return ((BlockBreakEvent) e).getPlayer();
- else if (e instanceof PlayerEvent) return ((PlayerEvent) e).getPlayer();
- else if (e instanceof EntityEvent) return (Player) ((EntityEvent) e).getEntity();
- else if (e instanceof EnchantItemEvent) return ((EnchantItemEvent) e).getEnchanter();
+ if (e instanceof EntityDeathEvent) {
+ return ((EntityDeathEvent) e).getEntity().getKiller();
+ }
+ else if (e instanceof BlockBreakEvent) {
+ return ((BlockBreakEvent) e).getPlayer();
+ }
+ else if (e instanceof PlayerEvent) {
+ return ((PlayerEvent) e).getPlayer();
+ }
+ else if (e instanceof EntityEvent) {
+ return (Player) ((EntityEvent) e).getEntity();
+ }
+ else if (e instanceof EnchantItemEvent) {
+ return ((EnchantItemEvent) e).getEnchanter();
+ }
return null;
}
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/BackpackCrafter.java
index 07ea5acd5..c5e4edf6f 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/BackpackCrafter.java
@@ -1,10 +1,10 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks;
import java.util.List;
+import java.util.Optional;
import java.util.UUID;
import org.bukkit.Bukkit;
-import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
@@ -12,11 +12,13 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
+import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
-import io.github.thebusybiscuit.slimefun4.implementation.items.tools.SlimefunBackpack;
-import io.github.thebusybiscuit.slimefun4.implementation.listeners.BackpackListener;
+import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.multiblocks.MultiBlockMachine;
@@ -50,44 +52,57 @@ abstract class BackpackCrafter extends MultiBlockMachine {
}
int size = backpack.getSize();
- String id = retrieveID(backpackItem, size);
+ Optional id = retrieveID(backpackItem, size);
- if (id.equals("")) {
+ if (id.isPresent()) {
for (int line = 0; line < output.getItemMeta().getLore().size(); line++) {
- if (output.getItemMeta().getLore().get(line).equals(ChatColor.translateAlternateColorCodes('&', "&7ID: "))) {
- int backpackID = PlayerProfile.get(p).createBackpack(size).getID();
-
- BackpackListener.setBackpackId(p, output, line, backpackID);
- }
- }
- }
- else {
- for (int line = 0; line < output.getItemMeta().getLore().size(); line++) {
- if (output.getItemMeta().getLore().get(line).equals(ChatColor.translateAlternateColorCodes('&', "&7ID: "))) {
+ if (output.getItemMeta().getLore().get(line).equals(ChatColors.color("&7ID: "))) {
ItemMeta im = output.getItemMeta();
List lore = im.getLore();
- lore.set(line, lore.get(line).replace("", id));
+ lore.set(line, lore.get(line).replace("", id.get()));
im.setLore(lore);
output.setItemMeta(im);
break;
}
}
}
- }
+ else {
+ for (int line = 0; line < output.getItemMeta().getLore().size(); line++) {
+ if (output.getItemMeta().getLore().get(line).equals(ChatColors.color("&7ID: "))) {
+ int target = line;
- private String retrieveID(ItemStack backpack, int size) {
- if (backpack != null) {
- for (String line : backpack.getItemMeta().getLore()) {
- if (line.startsWith(ChatColor.translateAlternateColorCodes('&', "&7ID: ")) && line.contains("#")) {
- String id = line.replace(ChatColor.translateAlternateColorCodes('&', "&7ID: "), "");
- String[] idSplit = PatternUtils.HASH.split(id);
- PlayerProfile.fromUUID(UUID.fromString(idSplit[0])).getBackpack(Integer.parseInt(idSplit[1])).setSize(size);
- return id;
+ PlayerProfile.get(p, profile -> {
+ int backpackId = profile.createBackpack(size).getId();
+ SlimefunPlugin.getBackpackListener().setBackpackId(p, output, target, backpackId);
+ });
+
+ break;
}
}
}
-
- return "";
+ }
+
+ private Optional retrieveID(ItemStack backpack, int size) {
+ if (backpack != null) {
+ for (String line : backpack.getItemMeta().getLore()) {
+ if (line.startsWith(ChatColors.color("&7ID: ")) && line.contains("#")) {
+ String id = line.replace(ChatColors.color("&7ID: "), "");
+ String[] idSplit = PatternUtils.HASH.split(id);
+
+ PlayerProfile.fromUUID(UUID.fromString(idSplit[0]), profile -> {
+ Optional optional = profile.getBackpack(Integer.parseInt(idSplit[1]));
+
+ if (optional.isPresent()) {
+ optional.get().setSize(size);
+ }
+ });
+
+ return Optional.of(id);
+ }
+ }
+ }
+
+ return Optional.empty();
}
}
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 2d79a3601..8dead5313 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
@@ -12,7 +12,7 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
-import io.github.thebusybiscuit.slimefun4.implementation.items.tools.SlimefunBackpack;
+import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
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 b9154afc9..3ec940939 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
@@ -14,7 +14,7 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
-import io.github.thebusybiscuit.slimefun4.implementation.items.tools.SlimefunBackpack;
+import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/OreWasher.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/OreWasher.java
index ae47aaac3..a55e669ae 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/OreWasher.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/OreWasher.java
@@ -22,109 +22,98 @@ import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.multiblocks.MultiBlockMachine;
public class OreWasher extends MultiBlockMachine {
-
+
private final boolean legacyMode;
- public OreWasher(Category category) {
- super(category, SlimefunItems.ORE_WASHER,
- new ItemStack[] {null, new ItemStack(Material.DISPENSER), null, null, new ItemStack(Material.OAK_FENCE), null, null, new ItemStack(Material.CAULDRON), null},
- new ItemStack[] {
- SlimefunItems.SIFTED_ORE, SlimefunItems.IRON_DUST,
- SlimefunItems.SIFTED_ORE, SlimefunItems.GOLD_DUST,
- SlimefunItems.SIFTED_ORE, SlimefunItems.COPPER_DUST,
- SlimefunItems.SIFTED_ORE, SlimefunItems.TIN_DUST,
- SlimefunItems.SIFTED_ORE, SlimefunItems.ZINC_DUST,
- SlimefunItems.SIFTED_ORE, SlimefunItems.ALUMINUM_DUST,
- SlimefunItems.SIFTED_ORE, SlimefunItems.MAGNESIUM_DUST,
- SlimefunItems.SIFTED_ORE, SlimefunItems.LEAD_DUST,
- SlimefunItems.SIFTED_ORE, SlimefunItems.SILVER_DUST
- },
- BlockFace.SELF
- );
-
- legacyMode = SlimefunPlugin.getCfg().getBoolean("options.legacy-ore-washer");
- }
-
- @Override
- public List getDisplayRecipes() {
- return recipes.stream().map(items -> items[0]).collect(Collectors.toList());
- }
-
- @Override
- public void onInteract(Player p, Block b) {
- Block dispBlock = b.getRelative(BlockFace.UP);
- Dispenser disp = (Dispenser) dispBlock.getState();
- Inventory inv = disp.getInventory();
+ public OreWasher(Category category) {
+ super(category, SlimefunItems.ORE_WASHER, new ItemStack[] { null, new ItemStack(Material.DISPENSER), null, null, new ItemStack(Material.OAK_FENCE), null, null, new ItemStack(Material.CAULDRON), null }, new ItemStack[] { SlimefunItems.SIFTED_ORE, SlimefunItems.IRON_DUST, SlimefunItems.SIFTED_ORE, SlimefunItems.GOLD_DUST, SlimefunItems.SIFTED_ORE, SlimefunItems.COPPER_DUST, SlimefunItems.SIFTED_ORE, SlimefunItems.TIN_DUST, SlimefunItems.SIFTED_ORE, SlimefunItems.ZINC_DUST, SlimefunItems.SIFTED_ORE, SlimefunItems.ALUMINUM_DUST, SlimefunItems.SIFTED_ORE, SlimefunItems.MAGNESIUM_DUST, SlimefunItems.SIFTED_ORE, SlimefunItems.LEAD_DUST, SlimefunItems.SIFTED_ORE, SlimefunItems.SILVER_DUST }, BlockFace.SELF);
- for (ItemStack current : inv.getContents()) {
- if (current != null) {
- if (SlimefunUtils.isItemSimilar(current, SlimefunItems.SIFTED_ORE, true)) {
- ItemStack adding = getRandomDust();
- Inventory outputInv = null;
+ legacyMode = SlimefunPlugin.getCfg().getBoolean("options.legacy-ore-washer");
+ }
- if (!legacyMode) {
- // This is a fancy way of checking if there is empty space in the inv; by checking if an unobtainable item could fit in it.
- // However, due to the way the method findValidOutputInv() functions, the dummyAdding will never actually be added to the real inventory,
- // so it really doesn't matter what item the ItemStack is made by. SlimefunItems.DEBUG_FISH however, signals that it's
- // not supposed to be given to the player.
- ItemStack dummyAdding = SlimefunItems.DEBUG_FISH;
- outputInv = findOutputInventory(dummyAdding, dispBlock, inv);
- }
- else outputInv = findOutputInventory(adding, dispBlock, inv);
+ @Override
+ public List getDisplayRecipes() {
+ return recipes.stream().map(items -> items[0]).collect(Collectors.toList());
+ }
- if (outputInv != null) {
- ItemStack removing = current.clone();
- removing.setAmount(1);
- inv.removeItem(removing);
- outputInv.addItem(adding);
- p.getWorld().playSound(b.getLocation(), Sound.ENTITY_PLAYER_SPLASH, 1, 1);
- p.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, Material.WATER);
- if (InvUtils.fits(outputInv, SlimefunItems.STONE_CHUNK)) outputInv.addItem(SlimefunItems.STONE_CHUNK);
- }
- else SlimefunPlugin.getLocal().sendMessage(p, "machines.full-inventory", true);
-
- return;
- }
- else if (SlimefunUtils.isItemSimilar(current, new ItemStack(Material.SAND, 4), false)) {
- ItemStack adding = SlimefunItems.SALT;
- Inventory outputInv = findOutputInventory(adding, dispBlock, inv);
+ @Override
+ public void onInteract(Player p, Block b) {
+ Block dispBlock = b.getRelative(BlockFace.UP);
+ Dispenser disp = (Dispenser) dispBlock.getState();
+ Inventory inv = disp.getInventory();
- if (outputInv != null) {
- ItemStack removing = current.clone();
- removing.setAmount(4);
- inv.removeItem(removing);
- outputInv.addItem(adding);
- p.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, Material.WATER);
- p.getWorld().playSound(b.getLocation(), Sound.ENTITY_PLAYER_SPLASH, 1, 1);
- }
- else SlimefunPlugin.getLocal().sendMessage(p, "machines.full-inventory", true);
+ for (ItemStack current : inv.getContents()) {
+ if (current != null) {
+ if (SlimefunUtils.isItemSimilar(current, SlimefunItems.SIFTED_ORE, true)) {
+ ItemStack output = getRandomDust();
+ Inventory outputInv = null;
- return;
- }
- else if (SlimefunUtils.isItemSimilar(current, SlimefunItems.PULVERIZED_ORE, true)) {
- ItemStack adding = SlimefunItems.PURE_ORE_CLUSTER;
- Inventory outputInv = findOutputInventory(adding, dispBlock, inv);
+ if (!legacyMode) {
+ // This is a fancy way of checking if there is empty space in the inv; by checking if an
+ // unobtainable item could fit in it.
+ // However, due to the way the method findValidOutputInv() functions, the dummyAdding will never
+ // actually be added to the real inventory,
+ // so it really doesn't matter what item the ItemStack is made by. SlimefunItems.DEBUG_FISH
+ // however, signals that it's
+ // not supposed to be given to the player.
+ ItemStack dummyAdding = SlimefunItems.DEBUG_FISH;
+ outputInv = findOutputInventory(dummyAdding, dispBlock, inv);
+ }
+ else outputInv = findOutputInventory(output, dispBlock, inv);
- if (outputInv != null) {
- ItemStack removing = current.clone();
- removing.setAmount(1);
- inv.removeItem(removing);
- outputInv.addItem(adding);
- p.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, Material.WATER);
- p.getWorld().playSound(b.getLocation(), Sound.ENTITY_PLAYER_SPLASH, 1, 1);
- }
- else SlimefunPlugin.getLocal().sendMessage(p, "machines.full-inventory", true);
+ if (outputInv != null) {
+ ItemStack removing = current.clone();
+ removing.setAmount(1);
+ inv.removeItem(removing);
+ outputInv.addItem(output);
+ p.getWorld().playSound(b.getLocation(), Sound.ENTITY_PLAYER_SPLASH, 1, 1);
+ p.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, Material.WATER);
+ if (InvUtils.fits(outputInv, SlimefunItems.STONE_CHUNK)) outputInv.addItem(SlimefunItems.STONE_CHUNK);
+ }
+ else SlimefunPlugin.getLocal().sendMessage(p, "machines.full-inventory", true);
- return;
- }
- }
- }
- SlimefunPlugin.getLocal().sendMessage(p, "machines.unknown-material", true);
- }
+ return;
+ }
+ else if (SlimefunUtils.isItemSimilar(current, new ItemStack(Material.SAND, 4), false)) {
+ ItemStack output = SlimefunItems.SALT;
+ Inventory outputInv = findOutputInventory(output, dispBlock, inv);
- public ItemStack getRandomDust() {
- int index = ThreadLocalRandom.current().nextInt(shownRecipes.size() / 2);
- return shownRecipes.get(index * 2 + 1).clone();
- }
+ if (outputInv != null) {
+ ItemStack removing = current.clone();
+ removing.setAmount(2);
+ inv.removeItem(removing);
+ outputInv.addItem(output);
+ p.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, Material.WATER);
+ p.getWorld().playSound(b.getLocation(), Sound.ENTITY_PLAYER_SPLASH, 1, 1);
+ }
+ else SlimefunPlugin.getLocal().sendMessage(p, "machines.full-inventory", true);
+
+ return;
+ }
+ else if (SlimefunUtils.isItemSimilar(current, SlimefunItems.PULVERIZED_ORE, true)) {
+ ItemStack output = SlimefunItems.PURE_ORE_CLUSTER;
+ Inventory outputInv = findOutputInventory(output, dispBlock, inv);
+
+ if (outputInv != null) {
+ ItemStack removing = current.clone();
+ removing.setAmount(1);
+ inv.removeItem(removing);
+ outputInv.addItem(output);
+ p.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, Material.WATER);
+ p.getWorld().playSound(b.getLocation(), Sound.ENTITY_PLAYER_SPLASH, 1, 1);
+ }
+ else SlimefunPlugin.getLocal().sendMessage(p, "machines.full-inventory", true);
+
+ return;
+ }
+ }
+ }
+ SlimefunPlugin.getLocal().sendMessage(p, "machines.unknown-material", true);
+ }
+
+ public ItemStack getRandomDust() {
+ int index = ThreadLocalRandom.current().nextInt(shownRecipes.size() / 2);
+ return shownRecipes.get(index * 2 + 1).clone();
+ }
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BackpackListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BackpackListener.java
index d5c047626..70d69518d 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BackpackListener.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/BackpackListener.java
@@ -21,9 +21,9 @@ import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.implementation.items.food.Cooler;
import io.github.thebusybiscuit.slimefun4.implementation.items.food.Juice;
-import io.github.thebusybiscuit.slimefun4.implementation.items.tools.SlimefunBackpack;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.Slimefun;
@@ -53,10 +53,12 @@ public class BackpackListener implements Listener {
@EventHandler
public void onClose(InventoryCloseEvent e) {
- if (backpacks.containsKey(e.getPlayer().getUniqueId())) {
- ((Player) e.getPlayer()).playSound(e.getPlayer().getLocation(), Sound.ENTITY_HORSE_ARMOR, 1F, 1F);
- PlayerProfile.getBackpack(backpacks.get(e.getPlayer().getUniqueId())).markDirty();
- backpacks.remove(e.getPlayer().getUniqueId());
+ Player p = ((Player) e.getPlayer());
+ ItemStack backpack = backpacks.remove(p.getUniqueId());
+
+ if (backpack != null) {
+ p.playSound(p.getLocation(), Sound.ENTITY_HORSE_ARMOR, 1F, 1F);
+ PlayerProfile.getBackpack(backpack, PlayerBackpack::markDirty);
}
}
@@ -131,7 +133,7 @@ public class BackpackListener implements Listener {
List lore = item.getItemMeta().getLore();
for (int line = 0; line < lore.size(); line++) {
if (lore.get(line).equals(ChatColors.color("&7ID: "))) {
- setBackpackId(p, item, line, profile.createBackpack(size).getID());
+ setBackpackId(p, item, line, profile.createBackpack(size).getId());
break;
}
}
@@ -140,9 +142,7 @@ public class BackpackListener implements Listener {
p.playSound(p.getLocation(), Sound.ENTITY_HORSE_ARMOR, 1F, 1F);
backpacks.put(p.getUniqueId(), item);
- Slimefun.runSync(() -> {
- PlayerBackpack backpack = PlayerProfile.getBackpack(item);
-
+ PlayerProfile.getBackpack(item, backpack -> {
if (backpack != null) {
backpack.open(p);
}
@@ -153,7 +153,7 @@ public class BackpackListener implements Listener {
}
}
- public static void setBackpackId(Player p, ItemStack item, int line, int id) {
+ public void setBackpackId(Player p, ItemStack item, int line, int id) {
ItemMeta im = item.getItemMeta();
List lore = im.getLore();
lore.set(line, lore.get(line).replace("", p.getUniqueId() + "#" + id));
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 1b9191f8e..317a98aed 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
@@ -20,9 +20,7 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.inventory.ItemStack;
-import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
-import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.HandledBlock;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@@ -163,25 +161,6 @@ public class BlockListener implements Listener {
}
}
- @EventHandler(ignoreCancelled = true)
- public void onBlockPlace(BlockPlaceEvent e) {
- ItemStack item = e.getItemInHand();
-
- if (SlimefunUtils.isItemSimilar(item, SlimefunItems.ADVANCED_CIRCUIT_BOARD, true)) e.setCancelled(true);
- else if (SlimefunUtils.isItemSimilar(item, SlimefunItems.CARBON, true)) e.setCancelled(true);
- else if (SlimefunUtils.isItemSimilar(item, SlimefunItems.COMPRESSED_CARBON, true)) e.setCancelled(true);
- else if (SlimefunUtils.isItemSimilar(item, SlimefunItems.CARBON_CHUNK, true)) e.setCancelled(true);
- else if (SlimefunUtils.isItemSimilar(item, SlimefunItems.ANDROID_MEMORY_CORE, true)) e.setCancelled(true);
- else if (SlimefunUtils.isItemSimilar(item, SlimefunItems.LAVA_CRYSTAL, true)) e.setCancelled(true);
- else if (SlimefunUtils.isItemSimilar(item, SlimefunItems.TINY_URANIUM, true)) e.setCancelled(true);
- else if (SlimefunUtils.isItemSimilar(item, SlimefunItems.SMALL_URANIUM, true)) e.setCancelled(true);
- else if (SlimefunUtils.isItemSimilar(item, SlimefunItems.BROKEN_SPAWNER, false)) e.setCancelled(true);
- else if (e.getBlock().getY() != e.getBlockAgainst().getY() && (SlimefunUtils.isItemSimilar(item, SlimefunItems.CARGO_INPUT, false) || SlimefunUtils.isItemSimilar(item, SlimefunItems.CARGO_OUTPUT, false) || SlimefunUtils.isItemSimilar(item, SlimefunItems.CARGO_OUTPUT_ADVANCED, false))) {
- SlimefunPlugin.getLocal().sendMessage(e.getPlayer(), "machines.CARGO_NODES.must-be-placed", true);
- e.setCancelled(true);
- }
- }
-
private int getBonusDropsWithFortune(ItemStack item, Block b) {
int fortune = 1;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/CargoNodeListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/CargoNodeListener.java
new file mode 100644
index 000000000..0381aa4a4
--- /dev/null
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/CargoNodeListener.java
@@ -0,0 +1,38 @@
+package io.github.thebusybiscuit.slimefun4.implementation.listeners;
+
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.inventory.ItemStack;
+
+import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
+
+/**
+ * This {@link Listener} is solely responsible for preventing Cargo Nodes from being placed
+ * on the top or bottom of a block.
+ *
+ * @author TheBusyBiscuit
+ *
+ */
+public class CargoNodeListener implements Listener {
+
+ public CargoNodeListener(SlimefunPlugin plugin) {
+ plugin.getServer().getPluginManager().registerEvents(this, plugin);
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ public void onCargoNodePlace(BlockPlaceEvent e) {
+ if (e.getBlock().getY() != e.getBlockAgainst().getY() && isCargoNode(e.getItemInHand())) {
+ SlimefunPlugin.getLocal().sendMessage(e.getPlayer(), "machines.CARGO_NODES.must-be-placed", true);
+ e.setCancelled(true);
+ }
+ }
+
+ private boolean isCargoNode(ItemStack item) {
+ return SlimefunUtils.isItemSimilar(item, SlimefunItems.CARGO_INPUT, false)
+ || SlimefunUtils.isItemSimilar(item, SlimefunItems.CARGO_OUTPUT, false)
+ || SlimefunUtils.isItemSimilar(item, SlimefunItems.CARGO_OUTPUT_ADVANCED, false);
+ }
+}
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 a05a728e4..82a8c35d6 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
@@ -39,22 +39,22 @@ public class CoolerListener implements Listener {
}
@EventHandler
- public void onStarve(FoodLevelChangeEvent e) {
+ public void onHungerLoss(FoodLevelChangeEvent e) {
if (cooler == null || cooler.isDisabled()) {
return;
}
- if (e.getFoodLevel() < ((Player) e.getEntity()).getFoodLevel()) {
- Player p = (Player) e.getEntity();
+ Player p = (Player) e.getEntity();
+ if (e.getFoodLevel() < p.getFoodLevel()) {
for (ItemStack item : p.getInventory().getContents()) {
if (cooler.isItem(item)) {
if (Slimefun.hasUnlocked(p, cooler, true)) {
- PlayerBackpack backpack = PlayerProfile.getBackpack(item);
-
- if (backpack != null && consumeJuice(p, backpack)) {
- break;
- }
+ PlayerProfile.getBackpack(item, backpack -> {
+ if (backpack != null) {
+ Slimefun.runSync(() -> consumeJuice(p, backpack));
+ }
+ });
}
else {
return;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/EnhancedFurnaceListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/EnhancedFurnaceListener.java
index bbbc4b484..a97f9801f 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/EnhancedFurnaceListener.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/EnhancedFurnaceListener.java
@@ -1,6 +1,7 @@
package io.github.thebusybiscuit.slimefun4.implementation.listeners;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
import org.bukkit.block.Furnace;
import org.bukkit.event.EventHandler;
@@ -27,6 +28,10 @@ import me.mrCookieSlime.Slimefun.api.BlockStorage;
*/
public class EnhancedFurnaceListener implements Listener {
+ // This will throttle the spam a bit, enough to irritate Server Owners so they report it
+ // but low enough to not cause them to rage quit
+ private long lastWarning = 0;
+
public EnhancedFurnaceListener(SlimefunPlugin plugin) {
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@@ -36,7 +41,14 @@ public class EnhancedFurnaceListener implements Listener {
SlimefunItem furnace = BlockStorage.check(e.getBlock());
if (furnace instanceof EnhancedFurnace && ((EnhancedFurnace) furnace).getFuelEfficiency() > 0) {
- e.setBurnTime(((EnhancedFurnace) furnace).getFuelEfficiency() * e.getBurnTime());
+ int burnTime = e.getBurnTime();
+ int newBurnTime = ((EnhancedFurnace) furnace).getFuelEfficiency() * burnTime;
+ e.setBurnTime(newBurnTime);
+
+ if (e.getBurnTime() < burnTime && lastWarning + TimeUnit.MINUTES.toMillis(10) < System.currentTimeMillis()) {
+ lastWarning = System.currentTimeMillis();
+ throw new IllegalStateException("Enhanced Furnace tried to increase burn time but actually decreased it: " + burnTime + " > " + e.getBurnTime() + " (supposed to be " + newBurnTime + ")");
+ }
}
}
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 dba39fed2..f8b58c187 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
@@ -10,6 +10,7 @@ import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
+import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
@@ -40,17 +41,21 @@ public class IronGolemListener implements Listener {
item = inv.getItemInOffHand();
}
- if (item != null && item.getType() == Material.IRON_INGOT && SlimefunItem.getByItem(item) != null) {
- e.setCancelled(true);
- SlimefunPlugin.getLocal().sendMessage(e.getPlayer(), "messages.no-iron-golem-heal");
+ if (item != null && item.getType() == Material.IRON_INGOT) {
+ SlimefunItem sfItem = SlimefunItem.getByItem(item);
- // 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) {
- inv.setItemInOffHand(item);
+ if (sfItem != null && !(sfItem instanceof VanillaItem)) {
+ e.setCancelled(true);
+ SlimefunPlugin.getLocal().sendMessage(e.getPlayer(), "messages.no-iron-golem-heal");
+
+ // 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) {
+ inv.setItemInOffHand(item);
+ }
}
}
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/NetworkListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/NetworkListener.java
index fe7e45317..c079d8ba7 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/NetworkListener.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/NetworkListener.java
@@ -7,7 +7,7 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import io.github.thebusybiscuit.slimefun4.api.network.Network;
-import io.github.thebusybiscuit.slimefun4.api.network.NetworkManager;
+import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
/**
@@ -23,8 +23,8 @@ public class NetworkListener implements Listener {
private final NetworkManager manager;
- public NetworkListener(SlimefunPlugin plugin) {
- manager = SlimefunPlugin.getNetworkManager();
+ public NetworkListener(SlimefunPlugin plugin, NetworkManager manager) {
+ this.manager = manager;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@@ -34,7 +34,7 @@ public class NetworkListener implements Listener {
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
- public void onPlaceBreak(BlockPlaceEvent e) {
+ public void onBlockPlace(BlockPlaceEvent e) {
manager.handleAllNetworkLocationUpdate(e.getBlock().getLocation());
}
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/VanillaMachinesListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/VanillaMachinesListener.java
index 5a977f315..17fe7ee4d 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/VanillaMachinesListener.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/VanillaMachinesListener.java
@@ -2,6 +2,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.listeners;
import org.bukkit.block.BrewingStand;
import org.bukkit.entity.Player;
+import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.CraftItemEvent;
@@ -13,9 +14,8 @@ import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide;
-import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
+import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
-import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
/**
@@ -37,7 +37,8 @@ public class VanillaMachinesListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onGrindstone(InventoryClickEvent e) {
// The Grindstone was only ever added in MC 1.14
- if (!SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
+ MinecraftVersion minecraftVersion = SlimefunPlugin.getMinecraftVersion();
+ if (!minecraftVersion.isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
return;
}
@@ -46,9 +47,10 @@ public class VanillaMachinesListener implements Listener {
ItemStack item2 = e.getInventory().getContents()[1];
if (checkForUnallowedItems(item1, item2)) {
- e.setCancelled(true);
+ e.setResult(Result.DENY);
}
}
+
}
@EventHandler
@@ -57,7 +59,7 @@ public class VanillaMachinesListener implements Listener {
SlimefunItem sfItem = SlimefunItem.getByItem(item);
if (sfItem != null && !sfItem.isUseableInWorkbench()) {
- e.setCancelled(true);
+ e.setResult(Result.DENY);
SlimefunPlugin.getLocal().sendMessage((Player) e.getWhoClicked(), "workbench.not-enhanced", true);
break;
}
@@ -78,14 +80,14 @@ public class VanillaMachinesListener implements Listener {
}
}
- @EventHandler
+ @EventHandler(ignoreCancelled = true)
public void onAnvil(InventoryClickEvent e) {
if (e.getRawSlot() == 2 && e.getInventory().getType() == InventoryType.ANVIL && e.getWhoClicked() instanceof Player) {
ItemStack item1 = e.getInventory().getContents()[0];
ItemStack item2 = e.getInventory().getContents()[1];
- if (!SlimefunUtils.isItemSimilar(item1, SlimefunItems.ELYTRA, true) && checkForUnallowedItems(item1, item2)) {
- e.setCancelled(true);
+ if (checkForUnallowedItems(item1, item2)) {
+ e.setResult(Result.DENY);
SlimefunPlugin.getLocal().sendMessage((Player) e.getWhoClicked(), "anvil.not-working", true);
}
}
@@ -96,7 +98,7 @@ public class VanillaMachinesListener implements Listener {
Inventory inventory = e.getInventory();
if (inventory.getType() == InventoryType.BREWING && e.getRawSlot() < inventory.getSize() && inventory.getHolder() instanceof BrewingStand) {
- e.setCancelled(SlimefunItem.getByItem(e.getCursor()) != null);
+ e.setCancelled(isUnallowed(SlimefunItem.getByItem(e.getCursor())));
}
}
@@ -108,11 +110,15 @@ public class VanillaMachinesListener implements Listener {
SlimefunItem sfItem1 = SlimefunItem.getByItem(item1);
SlimefunItem sfItem2 = SlimefunItem.getByItem(item2);
- if ((sfItem1 != null && !sfItem1.isDisabled()) || (sfItem2 != null && !sfItem2.isDisabled())) {
+ if (isUnallowed(sfItem1) || isUnallowed(sfItem2)) {
return true;
}
}
return false;
}
+
+ private boolean isUnallowed(SlimefunItem item) {
+ return item != null && !(item instanceof VanillaItem) && !item.isDisabled();
+ }
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/DefaultCategories.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/DefaultCategories.java
index a893ddf8d..332d0d397 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/DefaultCategories.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/DefaultCategories.java
@@ -7,12 +7,12 @@ import org.bukkit.NamespacedKey;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.cscorelib2.skull.SkullItem;
+import io.github.thebusybiscuit.slimefun4.core.categories.LockedCategory;
import io.github.thebusybiscuit.slimefun4.core.categories.SeasonalCategory;
import io.github.thebusybiscuit.slimefun4.utils.ChatUtils;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
import me.mrCookieSlime.Slimefun.Objects.Category;
-import me.mrCookieSlime.Slimefun.Objects.LockedCategory;
/**
* This class holds a reference to every {@link Category}
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 657d41218..98ed5def8 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
@@ -87,13 +87,13 @@ public final class PostSetup {
CommandSender sender = Bukkit.getConsoleSender();
int total = SlimefunPlugin.getRegistry().getEnabledSlimefunItems().size();
- int vanilla = SlimefunPlugin.getRegistry().countVanillaItems();
+ int slimefunOnly = countNonAddonItems();
sender.sendMessage("");
sender.sendMessage(ChatColor.GREEN + "######################### - Slimefun v" + SlimefunPlugin.getVersion() + " - #########################");
sender.sendMessage("");
sender.sendMessage(ChatColor.GREEN + "Successfully loaded " + total + " Items and " + SlimefunPlugin.getRegistry().getResearches().size() + " Researches");
- sender.sendMessage(ChatColor.GREEN + "( " + vanilla + " Items from Slimefun, " + (total - vanilla) + " Items from " + SlimefunPlugin.getInstalledAddons().size() + " Addons )");
+ sender.sendMessage(ChatColor.GREEN + "( " + slimefunOnly + " Items from Slimefun, " + (total - slimefunOnly) + " Items from " + SlimefunPlugin.getInstalledAddons().size() + " Addons )");
sender.sendMessage("");
sender.sendMessage(ChatColor.GREEN + "Slimefun is an Open-Source project that is kept alive by a large community.");
sender.sendMessage(ChatColor.GREEN + "Consider helping us maintain this project by contributing on GitHub!");
@@ -117,6 +117,10 @@ public final class PostSetup {
SlimefunPlugin.getRegistry().setAutoLoadingMode(true);
}
+ private static int countNonAddonItems() {
+ return (int) SlimefunPlugin.getRegistry().getEnabledSlimefunItems().stream().filter(item -> item.getAddon() instanceof SlimefunPlugin).count();
+ }
+
private static void loadAutomaticCraftingChamber() {
AutomatedCraftingChamber crafter = (AutomatedCraftingChamber) SlimefunItems.AUTOMATED_CRAFTING_CHAMBER.getItem();
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/ResearchSetup.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/ResearchSetup.java
index c4a8cf2a2..175037c30 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/ResearchSetup.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/setup/ResearchSetup.java
@@ -4,11 +4,10 @@ import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
-import me.mrCookieSlime.Slimefun.Objects.Research;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
-import me.mrCookieSlime.Slimefun.api.Slimefun;
/**
* This static setup class is used to register all default implementations of
@@ -110,10 +109,10 @@ public final class ResearchSetup {
register("table_saw", 92, "Table Saw", 4, SlimefunItems.TABLE_SAW);
register("slime_steel_armor", 93, "Slimy Steel Armor", 27, SlimefunItems.SLIME_HELMET_STEEL, SlimefunItems.SLIME_CHESTPLATE_STEEL, SlimefunItems.SLIME_LEGGINGS_STEEL, SlimefunItems.SLIME_BOOTS_STEEL);
register("blade_of_vampires", 94, "Blade of Vampires", 26, SlimefunItems.BLADE_OF_VAMPIRES);
- Slimefun.registerResearch(new NamespacedKey(SlimefunPlugin.instance, "digital_miner"), 95, "Lazy Mining", 40, SlimefunItems.DIGITAL_MINER);
+ register("digital_miner", 95, "Lazy Mining", 40, SlimefunItems.DIGITAL_MINER);
register("water_staff", 96, "Water Staff", 8, SlimefunItems.STAFF_WATER);
register("24k_gold_block", 97, "Golden City", 19, SlimefunItems.GOLD_24K_BLOCK);
- Slimefun.registerResearch(new NamespacedKey(SlimefunPlugin.instance, "advanced_digital_miner"), 98, "Advanced Mining 101", 42, SlimefunItems.ADVANCED_DIGITAL_MINER);
+ register("advanced_digital_miner", 98, "Advanced Mining 101", 42, SlimefunItems.ADVANCED_DIGITAL_MINER);
register("composter", 99, "Composting Dirt", 3, SlimefunItems.COMPOSTER);
register("farmer_shoes", 100, "Farmer Shoes", 4, SlimefunItems.FARMER_SHOES);
register("explosive_tools", 101, "Explosive Tools", 30, SlimefunItems.EXPLOSIVE_PICKAXE, SlimefunItems.EXPLOSIVE_SHOVEL);
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 198a979b8..7a54bcf36 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
@@ -41,6 +41,9 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.androids.Programm
import io.github.thebusybiscuit.slimefun4.implementation.items.androids.WoodcutterAndroid;
import io.github.thebusybiscuit.slimefun4.implementation.items.armor.Parachute;
import io.github.thebusybiscuit.slimefun4.implementation.items.armor.SlimefunArmorPiece;
+import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.RestoredBackpack;
+import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack;
+import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SoulboundBackpack;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.BlockPlacer;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.Composter;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.Crucible;
@@ -49,6 +52,7 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.HologramPr
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;
import io.github.thebusybiscuit.slimefun4.implementation.items.blocks.WitherProofBlock;
import io.github.thebusybiscuit.slimefun4.implementation.items.cargo.AdvancedCargoOutputNode;
import io.github.thebusybiscuit.slimefun4.implementation.items.cargo.CargoConnectorNode;
@@ -125,7 +129,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedMa
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.SoulboundBackpack;
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;
@@ -166,7 +169,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.items.tools.PickaxeOfTh
import io.github.thebusybiscuit.slimefun4.implementation.items.tools.PickaxeOfVeinMining;
import io.github.thebusybiscuit.slimefun4.implementation.items.tools.PortableCrafter;
import io.github.thebusybiscuit.slimefun4.implementation.items.tools.PortableDustbin;
-import io.github.thebusybiscuit.slimefun4.implementation.items.tools.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.implementation.items.tools.SmeltersPickaxe;
import io.github.thebusybiscuit.slimefun4.implementation.items.weapons.ExplosiveBow;
import io.github.thebusybiscuit.slimefun4.implementation.items.weapons.IcyBow;
@@ -374,7 +376,7 @@ public final class SlimefunItemSetup {
new ItemStack[] {null, null, null, null, new CustomItem(SkullItem.fromBase64("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODkwOTFkNzllYTBmNTllZjdlZjk0ZDdiYmE2ZTVmMTdmMmY3ZDQ1NzJjNDRmOTBmNzZjNDgxOWE3MTQifX19"), "&aIron Golem"), null, null, null, null})
.register(plugin);
- new SlimefunItem(categories.technicalComponents, (SlimefunItemStack) SlimefunItems.ADVANCED_CIRCUIT_BOARD, RecipeType.ENHANCED_CRAFTING_TABLE,
+ new UnplaceableBlock(categories.technicalComponents, (SlimefunItemStack) SlimefunItems.ADVANCED_CIRCUIT_BOARD, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {new ItemStack(Material.LAPIS_BLOCK), new ItemStack(Material.LAPIS_BLOCK), new ItemStack(Material.LAPIS_BLOCK), new ItemStack(Material.REDSTONE_BLOCK), SlimefunItems.BASIC_CIRCUIT_BOARD, new ItemStack(Material.REDSTONE_BLOCK), new ItemStack(Material.LAPIS_BLOCK), new ItemStack(Material.LAPIS_BLOCK), new ItemStack(Material.LAPIS_BLOCK)})
.register(plugin);
@@ -565,7 +567,7 @@ public final class SlimefunItemSetup {
new ItemStack[] {new ItemStack(Material.NETHERRACK, 16), null, null, null, null, null, null, null, null})
.register(plugin);
- new SlimefunItem(categories.resources, (SlimefunItemStack) SlimefunItems.CARBON, RecipeType.COMPRESSOR,
+ new UnplaceableBlock(categories.resources, (SlimefunItemStack) SlimefunItems.CARBON, RecipeType.COMPRESSOR,
new ItemStack[] {new ItemStack(Material.COAL, 8), null, null, null, null, null, null, null, null})
.register(plugin);
@@ -577,11 +579,11 @@ public final class SlimefunItemSetup {
new ItemStack[] {new CustomItem(SlimefunItems.STEEL_INGOT, 8), null, null, null, null, null, null, null, null})
.register(plugin);
- new SlimefunItem(categories.resources, (SlimefunItemStack) SlimefunItems.COMPRESSED_CARBON, RecipeType.COMPRESSOR,
+ new UnplaceableBlock(categories.resources, (SlimefunItemStack) SlimefunItems.COMPRESSED_CARBON, RecipeType.COMPRESSOR,
new ItemStack[] {new CustomItem(SlimefunItems.CARBON, 4), null, null, null, null, null, null, null, null})
.register(plugin);
- new SlimefunItem(categories.resources, (SlimefunItemStack) SlimefunItems.CARBON_CHUNK, RecipeType.ENHANCED_CRAFTING_TABLE,
+ new UnplaceableBlock(categories.resources, (SlimefunItemStack) SlimefunItems.CARBON_CHUNK, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.COMPRESSED_CARBON, SlimefunItems.COMPRESSED_CARBON, SlimefunItems.COMPRESSED_CARBON, SlimefunItems.COMPRESSED_CARBON, new ItemStack(Material.FLINT), SlimefunItems.COMPRESSED_CARBON, SlimefunItems.COMPRESSED_CARBON, SlimefunItems.COMPRESSED_CARBON, SlimefunItems.COMPRESSED_CARBON})
.register(plugin);
@@ -763,7 +765,7 @@ public final class SlimefunItemSetup {
new ItemStack[] {SlimefunItems.REINFORCED_ALLOY_INGOT, SlimefunItems.SOLAR_PANEL, SlimefunItems.REINFORCED_ALLOY_INGOT, SlimefunItems.REINFORCED_ALLOY_INGOT, null, SlimefunItems.REINFORCED_ALLOY_INGOT, SlimefunItems.MEDIUM_CAPACITOR, null, SlimefunItems.MEDIUM_CAPACITOR})
.register(plugin);
- new SlimefunItem(categories.magicalResources, (SlimefunItemStack) SlimefunItems.LAVA_CRYSTAL, RecipeType.ENHANCED_CRAFTING_TABLE,
+ new UnplaceableBlock(categories.magicalResources, (SlimefunItemStack) SlimefunItems.LAVA_CRYSTAL, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.MAGIC_LUMP_1, new ItemStack(Material.BLAZE_POWDER), SlimefunItems.MAGIC_LUMP_1, new ItemStack(Material.BLAZE_POWDER), SlimefunItems.RUNE_FIRE, new ItemStack(Material.BLAZE_POWDER), SlimefunItems.MAGIC_LUMP_1, new ItemStack(Material.BLAZE_POWDER), SlimefunItems.MAGIC_LUMP_1})
.register(plugin);
@@ -917,7 +919,7 @@ public final class SlimefunItemSetup {
new ItemStack[] {SlimefunItems.PULVERIZED_ORE, null, null, null, null, null, null, null, null})
.register(plugin);
- new SlimefunItem(categories.misc, (SlimefunItemStack) SlimefunItems.TINY_URANIUM, RecipeType.ORE_CRUSHER,
+ new UnplaceableBlock(categories.misc, (SlimefunItemStack) SlimefunItems.TINY_URANIUM, RecipeType.ORE_CRUSHER,
new ItemStack[] {SlimefunItems.PURE_ORE_CLUSTER, null, null, null, null, null, null, null, null})
.register(plugin);
@@ -1201,6 +1203,8 @@ public final class SlimefunItemSetup {
new ItemStack[] {SlimefunItems.GOLD_24K, null, SlimefunItems.GOLD_24K, new ItemStack(Material.LEATHER), SlimefunItems.GILDED_BACKPACK, new ItemStack(Material.LEATHER), SlimefunItems.GOLD_24K, null, SlimefunItems.GOLD_24K})
.register(plugin);
+ new RestoredBackpack(categories.usefulItems).register(plugin);
+
new SlimefunItem(categories.technicalComponents, (SlimefunItemStack) SlimefunItems.MAGNET, RecipeType.SMELTERY,
new ItemStack[] {SlimefunItems.NICKEL_INGOT, SlimefunItems.ALUMINUM_DUST, SlimefunItems.IRON_DUST, SlimefunItems.COBALT_INGOT, null, null, null, null, null})
.register(plugin);
@@ -1221,7 +1225,7 @@ public final class SlimefunItemSetup {
new ItemStack[] {SlimefunItems.ENDER_LUMP_3, SlimefunItems.RUNE_AIR, SlimefunItems.ENDER_LUMP_3, SlimefunItems.RUNE_EARTH, SlimefunItems.NECROTIC_SKULL, SlimefunItems.RUNE_FIRE, SlimefunItems.ENDER_LUMP_3, SlimefunItems.RUNE_WATER, SlimefunItems.ENDER_LUMP_3})
.register(plugin);
- new SoulboundBackpack(36, categories.usefulItems, SlimefunItems.BOUND_BACKPACK,
+ new SoulboundBackpack(36, categories.usefulItems, SlimefunItems.BOUND_BACKPACK, RecipeType.MAGIC_WORKBENCH,
new ItemStack[] {SlimefunItems.ENDER_LUMP_2, null, SlimefunItems.ENDER_LUMP_2, SlimefunItems.ESSENCE_OF_AFTERLIFE, SlimefunItems.WOVEN_BACKPACK, SlimefunItems.ESSENCE_OF_AFTERLIFE, SlimefunItems.ENDER_LUMP_2, null, SlimefunItems.ENDER_LUMP_2})
.register(plugin);
@@ -1345,7 +1349,7 @@ public final class SlimefunItemSetup {
new ItemStack[] {new ItemStack(Material.GOLDEN_APPLE), null, null, null, null, null, null, null, null})
.register(plugin);
- new SlimefunItem(categories.magicalResources, (SlimefunItemStack) SlimefunItems.BROKEN_SPAWNER, new RecipeType(new NamespacedKey(plugin, "pickaxe_of_containment"), SlimefunItems.PICKAXE_OF_CONTAINMENT),
+ new UnplaceableBlock(categories.magicalResources, (SlimefunItemStack) SlimefunItems.BROKEN_SPAWNER, new RecipeType(new NamespacedKey(plugin, "pickaxe_of_containment"), SlimefunItems.PICKAXE_OF_CONTAINMENT),
new ItemStack[] {null, null, null, null, new ItemStack(Material.SPAWNER), null, null, null, null})
.register(plugin);
@@ -2207,7 +2211,7 @@ public final class SlimefunItemSetup {
new ItemStack[] {null, null, null, null, SlimefunItems.BUCKET_OF_OIL, null, null, null, null})
.register(plugin);
- new SlimefunItem(categories.technicalComponents, (SlimefunItemStack) SlimefunItems.ANDROID_MEMORY_CORE, RecipeType.ENHANCED_CRAFTING_TABLE,
+ new UnplaceableBlock(categories.technicalComponents, SlimefunItems.ANDROID_MEMORY_CORE, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.BRASS_INGOT, new ItemStack(Material.ORANGE_STAINED_GLASS), SlimefunItems.BRASS_INGOT, SlimefunItems.POWER_CRYSTAL, SlimefunItems.TIN_DUST, SlimefunItems.POWER_CRYSTAL, SlimefunItems.BRASS_INGOT, new ItemStack(Material.ORANGE_STAINED_GLASS), SlimefunItems.BRASS_INGOT})
.register(plugin);
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 097dbd74a..16ccbd6f0 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
@@ -39,6 +39,8 @@ public class JetpackTask extends PlayerTask {
p.setVelocity(vector);
}
- else Bukkit.getScheduler().cancelTask(id);
+ else {
+ Bukkit.getScheduler().cancelTask(id);
+ }
}
}
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 1687dc98f..abfd58f4c 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
@@ -62,7 +62,7 @@ public class SlimefunStartupTask implements Runnable {
}
if (isEnabled("ENERGY_REGULATOR", "CARGO_MANAGER")) {
- new NetworkListener(plugin);
+ new NetworkListener(plugin, SlimefunPlugin.getNetworkManager());
}
}
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 4a6f39798..13b0cf6a0 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/SlimefunUtils.java
@@ -1,17 +1,23 @@
package io.github.thebusybiscuit.slimefun4.utils;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.bukkit.ChatColor;
import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import org.bukkit.entity.Item;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.metadata.FixedMetadataValue;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
import io.github.thebusybiscuit.cscorelib2.item.ImmutableItemMeta;
+import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
+import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactive;
import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound;
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientPedestal;
import me.mrCookieSlime.EmeraldEnchants.EmeraldEnchants;
@@ -33,9 +39,11 @@ 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 SOULBOUND_LORE = ChatColor.GRAY + "Soulbound";
private static final String NO_PICKUP_METADATA = "no_pickup";
+ private static final NamespacedKey SOULBOUND_KEY = new NamespacedKey(SlimefunPlugin.instance, "soulbound");
+ private static final String SOULBOUND_LORE = ChatColor.GRAY + "Soulbound";
+
private SlimefunUtils() {}
/**
@@ -75,33 +83,96 @@ public final class SlimefunUtils {
return false;
}
else {
- SlimefunItem backpack = SlimefunItems.BOUND_BACKPACK.getItem();
+ ItemMeta meta = item.hasItemMeta() ? item.getItemMeta() : null;
- if (backpack != null && backpack.isItem(item)) {
- return !backpack.isDisabled();
- }
- else {
- ItemStack strippedItem = item.clone();
+ if (meta != null && SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
+ PersistentDataContainer container = meta.getPersistentDataContainer();
- if (SlimefunPlugin.getThirdPartySupportService().isEmeraldEnchantsInstalled()) {
- for (ItemEnchantment enchantment : EmeraldEnchants.getInstance().getRegistry().getEnchantments(item)) {
- EmeraldEnchants.getInstance().getRegistry().applyEnchantment(strippedItem, enchantment.getEnchantment(), 0);
- }
- }
-
- SlimefunItem sfItem = SlimefunItem.getByItem(strippedItem);
-
- if (sfItem instanceof Soulbound && !sfItem.isDisabled()) {
+ if (container.has(SOULBOUND_KEY, PersistentDataType.BYTE)) {
return true;
}
- else if (item.hasItemMeta()) {
- ItemMeta im = item.getItemMeta();
- return (im.hasLore() && im.getLore().contains(SOULBOUND_LORE));
- }
+ }
- return false;
+ 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) {
+ return !sfItem.isDisabled();
+ }
+ else if (meta != null) {
+ return meta.hasLore() && meta.getLore().contains(SOULBOUND_LORE);
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Toggles an {@link ItemStack} to be Soulbound.
+ * If true is passed, this will add the {@link #SOULBOUND_LORE} and
+ * add a {@link NamespacedKey} to the item so it can be quickly identified
+ * by {@link #isSoulbound(ItemStack)}.
+ * If false is passed, this property will be removed.
+ *
+ * @param item
+ * The {@link ItemStack} you want to add/remove Soulbound from.
+ * @param makeSoulbound
+ * If they item should be soulbound.
+ *
+ * @see #isSoulbound(ItemStack)
+ */
+ public static void setSoulbound(ItemStack item, boolean makeSoulbound) {
+ if (item == null || item.getType() == Material.AIR) {
+ throw new IllegalArgumentException("A soulbound item cannot be null or air!");
+ }
+
+ boolean isSoulbound = isSoulbound(item);
+ ItemMeta meta = item.getItemMeta();
+
+ if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) {
+ PersistentDataContainer container = meta.getPersistentDataContainer();
+
+ if (makeSoulbound && !isSoulbound) {
+ container.set(SOULBOUND_KEY, PersistentDataType.BYTE, (byte) 1);
+ }
+
+ if (!makeSoulbound && isSoulbound) {
+ container.remove(SOULBOUND_KEY);
}
}
+
+ List lore = meta.hasLore() ? meta.getLore() : new ArrayList<>();
+
+ if (makeSoulbound && !isSoulbound) {
+ lore.add(SOULBOUND_LORE);
+ }
+
+ if (!makeSoulbound && isSoulbound) {
+ lore.remove(SOULBOUND_LORE);
+ }
+
+ meta.setLore(lore);
+ item.setItemMeta(meta);
+ }
+
+ /**
+ * This method checks whether the given {@link ItemStack} is radioactive.
+ *
+ * @param item
+ * The {@link ItemStack} to check
+ *
+ * @return Whether this {@link ItemStack} is radioactive or not
+ */
+ public static boolean isRadioactive(ItemStack item) {
+ return SlimefunItem.getByItem(item) instanceof Radioactive;
}
public static boolean containsSimilarItem(Inventory inventory, ItemStack itemStack, boolean checkLore) {
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 4ac3a73ac..e248c5d2e 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
@@ -21,7 +21,8 @@ public final class ItemStackWrapper extends ItemStack {
private ItemMeta meta;
public ItemStackWrapper(ItemStack item) {
- super(item);
+ super(item.getType());
+ meta = item.getItemMeta();
}
@Override
@@ -30,10 +31,6 @@ public final class ItemStackWrapper extends ItemStack {
// Since this class is immutable, we can simply let the super class create one copy
// and then store that instead of creating a clone everytime.
// This will significantly speed up any loop comparisons if used correctly.
- if (meta == null) {
- meta = super.getItemMeta();
- }
-
return meta;
}
@@ -44,7 +41,7 @@ public final class ItemStackWrapper extends ItemStack {
@Override
public boolean equals(Object obj) {
- throw new UnsupportedOperationException("ItemStackWrapper do not allow .equals()");
+ throw new UnsupportedOperationException("ItemStackWrappers do not allow .equals()");
}
@Override
diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Lists/SlimefunItems.java b/src/main/java/me/mrCookieSlime/Slimefun/Lists/SlimefunItems.java
index ff8802e51..077109586 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/Lists/SlimefunItems.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/Lists/SlimefunItems.java
@@ -12,7 +12,6 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
-import io.github.thebusybiscuit.cscorelib2.skull.SkullItem;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.core.attributes.MachineTier;
import io.github.thebusybiscuit.slimefun4.core.attributes.MachineType;
@@ -73,6 +72,8 @@ public final class SlimefunItems {
public static final ItemStack RADIANT_BACKPACK = new SlimefunItemStack("RADIANT_BACKPACK", "40cb1e67b512ab2d4bf3d7ace0eaaf61c32cd4681ddc3987ceb326706a33fa", "&eRadiant Backpack", "", "&7Size: &e54 (Double chest)", "&7ID: ", "", "&7&eRight Click&7 to open");
public static final SlimefunItemStack BOUND_BACKPACK = new SlimefunItemStack("BOUND_BACKPACK", "2a3b34862b9afb63cf8d5779966d3fba70af82b04e83f3eaf6449aeba", "&cSoulbound Backpack", "", "&7Size: &e36", "&7ID: ", "", "&7&eRight Click&7 to open");
public static final SlimefunItemStack COOLER = new SlimefunItemStack("COOLER", "d4c1572584eb5de229de9f5a4f779d0aacbaffd33bcb33eb4536a6a2bc6a1", "&bCooler", "&rAllows you to store Juices/Smoothies", "&rand automatically consumes them when you are hungry", "&rand you have this in your Inventory", "", "&7Size: &e27", "&7ID: ", "", "&7&eRight Click&7 to open");
+ public static final SlimefunItemStack RESTORED_BACKPACK = new SlimefunItemStack("RESTORED_BACKPACK", "40cb1e67b512ab2d4bf3d7ace0eaaf61c32cd4681ddc3987ceb326706a33fa", "&eRestored Backpack", "", "&7Retrieve your lost items", "&7ID: ", "", "&7&eRight Click&7 to open");
+
/* Jetpacks */
public static final SlimefunItemStack DURALUMIN_JETPACK = new SlimefunItemStack("DURALUMIN_JETPACK", Material.LEATHER_CHESTPLATE, Color.SILVER, "&9Electric Jetpack &7- &eI", "", "&8\u21E8 &7Material: &bDuralumin", "&c&o&8\u21E8 &e\u26A1 &70 / 20 J", "&8\u21E8 &7Thrust: &c0.35", "", "&7Hold &eShift&7 to use");
@@ -660,7 +661,7 @@ public final class SlimefunItems {
public static final SlimefunItemStack REFINERY = new SlimefunItemStack("REFINERY", Material.PISTON, "&cRefinery", "", "&rRefines Oil to create Fuel");
public static final SlimefunItemStack COMBUSTION_REACTOR = new SlimefunItemStack("COMBUSTION_REACTOR", "9343ce58da54c79924a2c9331cfc417fe8ccbbea9be45a7ac85860a6c730", "&cCombustion Reactor", "", LoreBuilder.machine(MachineTier.ADVANCED, MachineType.GENERATOR), LoreBuilder.powerBuffer(256), LoreBuilder.powerPerSecond(24));
- public static final ItemStack ANDROID_MEMORY_CORE = new SlimefunItemStack("ANDROID_MEMORY_CORE", "d78f2b7e5e75639ea7fb796c35d364c4df28b4243e66b76277aadcd6261337", "&bAndroid Memory Core");
+ public static final SlimefunItemStack ANDROID_MEMORY_CORE = new SlimefunItemStack("ANDROID_MEMORY_CORE", "d78f2b7e5e75639ea7fb796c35d364c4df28b4243e66b76277aadcd6261337", "&bAndroid Memory Core");
public static final SlimefunItemStack GPS_TELEPORTER_PYLON = new SlimefunItemStack("GPS_TELEPORTER_PYLON", Material.PURPLE_STAINED_GLASS, "&5GPS Teleporter Pylon", "", "&7Teleporter Component");
public static final SlimefunItemStack GPS_TELEPORTATION_MATRIX = new SlimefunItemStack("GPS_TELEPORTATION_MATRIX", Material.IRON_BLOCK, "&bGPS Teleporter Matrix", "", "&rThis is your Teleporter's Main Component", "&rThis Matrix allows Players to choose from all", "&rWaypoints made by the Player who has placed", "&rthis Device.");
@@ -710,27 +711,27 @@ public final class SlimefunItems {
public static final SlimefunItemStack AUTO_BREEDER = new SlimefunItemStack("AUTO_BREEDER", Material.HAY_BLOCK, "&eAuto-Breeder", "", "&rRuns on &aOrganic Food", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.powerBuffer(1024), "&8\u21E8 &e\u26A1 &760 J/Animal");
- public static final ItemStack ORGANIC_FOOD = new CustomItem(SkullItem.fromBase64("b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79"), "&aOrganic Food", "&7Content: &9X");
- public static final SlimefunItemStack WHEAT_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_WHEAT", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9Wheat");
- public static final SlimefunItemStack CARROT_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_CARROT", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9Carrots");
- public static final SlimefunItemStack POTATO_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_POTATO", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9Potatoes");
- public static final SlimefunItemStack SEEDS_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_SEEDS", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9Seeds");
- public static final SlimefunItemStack BEETROOT_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_BEETROOT", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9Beetroot");
- public static final SlimefunItemStack MELON_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_MELON", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9Melon");
- public static final SlimefunItemStack APPLE_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_APPLE", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9Apple");
- public static final SlimefunItemStack SWEET_BERRIES_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_SWEET_BERRIES", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9Sweet Berries");
- public static final SlimefunItemStack KELP_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_KELP", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9Dried Kelp");
+ public static final SlimefunItemStack ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Food", "&7Content: &9???");
+ public static final SlimefunItemStack WHEAT_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_WHEAT", ORGANIC_FOOD, "&aOrganic Food", "&7Content: &9Wheat");
+ public static final SlimefunItemStack CARROT_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_CARROT", ORGANIC_FOOD, "&aOrganic Food", "&7Content: &9Carrots");
+ public static final SlimefunItemStack POTATO_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_POTATO", ORGANIC_FOOD, "&aOrganic Food", "&7Content: &9Potatoes");
+ public static final SlimefunItemStack SEEDS_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_SEEDS", ORGANIC_FOOD, "&aOrganic Food", "&7Content: &9Seeds");
+ public static final SlimefunItemStack BEETROOT_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_BEETROOT", ORGANIC_FOOD, "&aOrganic Food", "&7Content: &9Beetroot");
+ public static final SlimefunItemStack MELON_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_MELON", ORGANIC_FOOD, "&aOrganic Food", "&7Content: &9Melon");
+ public static final SlimefunItemStack APPLE_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_APPLE", ORGANIC_FOOD, "&aOrganic Food", "&7Content: &9Apple");
+ public static final SlimefunItemStack SWEET_BERRIES_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_SWEET_BERRIES", ORGANIC_FOOD, "&aOrganic Food", "&7Content: &9Sweet Berries");
+ public static final SlimefunItemStack KELP_ORGANIC_FOOD = new SlimefunItemStack("ORGANIC_FOOD_KELP", ORGANIC_FOOD, "&aOrganic Food", "&7Content: &9Dried Kelp");
- public static final ItemStack FERTILIZER = new CustomItem(SkullItem.fromBase64("b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79"), "&aOrganic Fertilizer", "&7Content: &9X");
- public static final SlimefunItemStack WHEAT_FERTILIZER = new SlimefunItemStack("FERTILIZER_WHEAT", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9Wheat");
- public static final SlimefunItemStack CARROT_FERTILIZER = new SlimefunItemStack("FERTILIZER_CARROT", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9Carrots");
- public static final SlimefunItemStack POTATO_FERTILIZER = new SlimefunItemStack("FERTILIZER_POTATO", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9Potatoes");
- public static final SlimefunItemStack SEEDS_FERTILIZER = new SlimefunItemStack("FERTILIZER_SEEDS", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9Seeds");
- public static final SlimefunItemStack BEETROOT_FERTILIZER = new SlimefunItemStack("FERTILIZER_BEETROOT", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9Beetroot");
- public static final SlimefunItemStack MELON_FERTILIZER = new SlimefunItemStack("FERTILIZER_MELON", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9Melon");
- public static final SlimefunItemStack APPLE_FERTILIZER = new SlimefunItemStack("FERTILIZER_APPLE", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9Apple");
- public static final SlimefunItemStack SWEET_BERRIES_FERTILIZER = new SlimefunItemStack("FERTILIZER_SWEET_BERRIES", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9Sweet Berries");
- public static final SlimefunItemStack KELP_FERTILIZER = new SlimefunItemStack("FERTILIZER_KELP", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9Dried Kelp");
+ public static final SlimefunItemStack FERTILIZER = new SlimefunItemStack("FERTILIZER", "b439e3f5acbee9be4c4259289d6d9f35c635ffa661114687b3ea6dda8c79", "&aOrganic Fertilizer", "&7Content: &9???");
+ public static final SlimefunItemStack WHEAT_FERTILIZER = new SlimefunItemStack("FERTILIZER_WHEAT", FERTILIZER, "&aOrganic Fertilizer", "&7Content: &9Wheat");
+ public static final SlimefunItemStack CARROT_FERTILIZER = new SlimefunItemStack("FERTILIZER_CARROT", FERTILIZER, "&aOrganic Fertilizer", "&7Content: &9Carrots");
+ public static final SlimefunItemStack POTATO_FERTILIZER = new SlimefunItemStack("FERTILIZER_POTATO", FERTILIZER, "&aOrganic Fertilizer", "&7Content: &9Potatoes");
+ public static final SlimefunItemStack SEEDS_FERTILIZER = new SlimefunItemStack("FERTILIZER_SEEDS", FERTILIZER, "&aOrganic Fertilizer", "&7Content: &9Seeds");
+ public static final SlimefunItemStack BEETROOT_FERTILIZER = new SlimefunItemStack("FERTILIZER_BEETROOT", FERTILIZER, "&aOrganic Fertilizer", "&7Content: &9Beetroot");
+ public static final SlimefunItemStack MELON_FERTILIZER = new SlimefunItemStack("FERTILIZER_MELON", FERTILIZER, "&aOrganic Fertilizer", "&7Content: &9Melon");
+ public static final SlimefunItemStack APPLE_FERTILIZER = new SlimefunItemStack("FERTILIZER_APPLE", FERTILIZER, "&aOrganic Fertilizer", "&7Content: &9Apple");
+ public static final SlimefunItemStack SWEET_BERRIES_FERTILIZER = new SlimefunItemStack("FERTILIZER_SWEET_BERRIES", FERTILIZER, "&aOrganic Fertilizer", "&7Content: &9Sweet Berries");
+ public static final SlimefunItemStack KELP_FERTILIZER = new SlimefunItemStack("FERTILIZER_KELP", FERTILIZER, "&aOrganic Fertilizer", "&7Content: &9Dried Kelp");
public static final SlimefunItemStack ANIMAL_GROWTH_ACCELERATOR = new SlimefunItemStack("ANIMAL_GROWTH_ACCELERATOR", Material.HAY_BLOCK, "&bAnimal Growth Accelerator", "", "&rRuns on &aOrganic Food", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), LoreBuilder.powerBuffer(1024), LoreBuilder.powerPerSecond(28));
public static final SlimefunItemStack CROP_GROWTH_ACCELERATOR = new SlimefunItemStack("CROP_GROWTH_ACCELERATOR", Material.LIME_TERRACOTTA, "&aCrop Growth Accelerator", "", "&rRuns on &aOrganic Fertilizer", "", LoreBuilder.machine(MachineTier.END_GAME, MachineType.MACHINE), "&8\u21E8 &7Radius: 7x7", "&8\u21E8 &7Speed: &a3/time", LoreBuilder.powerBuffer(1024), LoreBuilder.powerPerSecond(50));
diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/Category.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/Category.java
index 6ca61b4aa..7a45f8535 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/Category.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/Category.java
@@ -6,6 +6,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
@@ -15,6 +16,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.core.categories.LockedCategory;
import io.github.thebusybiscuit.slimefun4.core.categories.SeasonalCategory;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
@@ -64,6 +66,9 @@ public class Category implements Keyed {
* the {@link SlimefunGuide}
*/
public Category(NamespacedKey key, ItemStack item, int tier) {
+ Validate.notNull(key, "A Category's NamespacedKey must not be null!");
+ Validate.notNull(item, "A Category's ItemStack must not be null!");
+
this.item = item;
this.key = key;
@@ -96,6 +101,13 @@ public class Category implements Keyed {
* the {@link SlimefunItem} that should be added to this {@link Category}
*/
public void add(SlimefunItem item) {
+ Validate.notNull(item, "Cannot add null Items to a Category!");
+
+ if (items.contains(item)) {
+ // Ignore duplicate entries
+ return;
+ }
+
items.add(item);
}
@@ -152,6 +164,18 @@ public class Category implements Keyed {
return items;
}
+ /**
+ * This method returns whether a given {@link SlimefunItem} exists in this {@link Category}.
+ *
+ * @param item
+ * The {@link SlimefunItem} to find
+ *
+ * @return Whether the given {@link SlimefunItem} was found in this {@link Category}
+ */
+ public boolean contains(SlimefunItem item) {
+ return item != null && items.contains(item);
+ }
+
/**
* Returns the tier of this {@link Category}.
* The tier determines the position of this {@link Category} in the {@link SlimefunGuide}.
diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/LockedCategory.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/LockedCategory.java
index 76be44aff..29a187c78 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/LockedCategory.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/LockedCategory.java
@@ -1,159 +1,20 @@
package me.mrCookieSlime.Slimefun.Objects;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.logging.Level;
-
-import org.apache.commons.lang.Validate;
import org.bukkit.NamespacedKey;
-import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
-import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
-import io.github.thebusybiscuit.slimefun4.core.categories.SeasonalCategory;
-import me.mrCookieSlime.Slimefun.SlimefunPlugin;
-import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
-import me.mrCookieSlime.Slimefun.api.Slimefun;
-
/**
- * Represents a {@link Category} that cannot be opened until the parent category/categories
- * are fully unlocked.
- *
- * See {@link Category} for the complete documentation.
- *
- * @author TheBusyBiscuit
- *
- * @see Category
- * @see SeasonalCategory
+ * @deprecated Moved to io.github.thebusybiscuit.slimefun4.core.categories.LockedCategory
*
*/
-public class LockedCategory extends Category {
+@Deprecated
+public class LockedCategory extends io.github.thebusybiscuit.slimefun4.core.categories.LockedCategory {
- private final NamespacedKey[] keys;
- private final Set parents = new HashSet<>();
-
- /**
- * The basic constructor for a LockedCategory.
- * Like {@link Category}, the default tier is automatically set to 3.
- *
- * @param key
- * A unique identifier for this category
- * @param item
- * The display item for this category
- * @param parents
- * The parent categories for this category
- *
- */
public LockedCategory(NamespacedKey key, ItemStack item, NamespacedKey... parents) {
this(key, item, 3, parents);
}
- /**
- * The constructor for a LockedCategory.
- *
- * @param key
- * A unique identifier for this category
- * @param item
- * The display item for this category
- * @param tier
- * The tier of this category
- * @param parents
- * The parent categories for this category
- *
- */
public LockedCategory(NamespacedKey key, ItemStack item, int tier, NamespacedKey... parents) {
- super(key, item, tier);
- Validate.noNullElements(parents, "A LockedCategory must not have any 'null' parents!");
-
- this.keys = parents;
- }
-
- @Override
- public void register() {
- super.register();
-
- List namespacedKeys = new ArrayList<>();
-
- for (NamespacedKey key : keys) {
- if (key != null) {
- namespacedKeys.add(key);
- }
- }
-
- for (Category category : SlimefunPlugin.getRegistry().getCategories()) {
- if (namespacedKeys.remove(category.getKey())) {
- addParent(category);
- }
- }
-
- for (NamespacedKey key : namespacedKeys) {
- Slimefun.getLogger().log(Level.INFO, "Parent \"{0}\" for Category \"{1}\" was not found, probably just disabled.", new Object[] { key, getKey() });
- }
- }
-
- /**
- * Gets the list of parent categories for this {@link LockedCategory}.
- *
- * @return the list of parent categories
- *
- * @see #addParent(Category)
- * @see #removeParent(Category)
- */
- public Set getParents() {
- return parents;
- }
-
- /**
- * Adds a parent {@link Category} to this {@link LockedCategory}.
- *
- * @param category
- * The {@link Category} to add as a parent
- *
- * @see #getParents()
- * @see #removeParent(Category)
- */
- public void addParent(Category category) {
- if (category == this || category == null) {
- throw new IllegalArgumentException("Category '" + item.getItemMeta().getDisplayName() + "' cannot be a parent of itself or have a 'null' parent.");
- }
-
- parents.add(category);
- }
-
- /**
- * Removes a {@link Category} from the parents of this {@link LockedCategory}.
- *
- * @param category
- * The {@link Category} to remove from the parents of this {@link LockedCategory}
- *
- * @see #getParents()
- * @see #addParent(Category)
- */
- public void removeParent(Category category) {
- parents.remove(category);
- }
-
- /**
- * Checks if the {@link Player} has fully unlocked all parent categories.
- *
- * @param p
- * The {@link Player} to check
- * @param profile
- * The {@link PlayerProfile} that belongs to the given {@link Player}
- * @return Whether the {@link Player} has fully completed all parent categories, otherwise false
- */
- public boolean hasUnlocked(Player p, PlayerProfile profile) {
- for (Category category : parents) {
- for (SlimefunItem item : category.getItems()) {
- // Should we replace this all with Slimefun.hasUnlocked() ?
- if (Slimefun.isEnabled(p, item, false) && Slimefun.hasPermission(p, item, false) && item.getResearch() != null && !profile.hasUnlocked(item.getResearch())) {
- return false;
- }
- }
- }
-
- return true;
+ super(key, item, tier, parents);
}
}
diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/Research.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/Research.java
index 22fee9042..eeb6ab35d 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/Research.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/Research.java
@@ -1,296 +1,15 @@
package me.mrCookieSlime.Slimefun.Objects;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.bukkit.Bukkit;
-import org.bukkit.GameMode;
-import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
-import org.bukkit.Sound;
-import org.bukkit.entity.Player;
-
-import io.github.thebusybiscuit.slimefun4.api.events.ResearchUnlockEvent;
-import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
-import io.github.thebusybiscuit.slimefun4.core.guide.options.SlimefunGuideSettings;
-import io.github.thebusybiscuit.slimefun4.core.services.localization.Language;
-import io.github.thebusybiscuit.slimefun4.implementation.setup.ResearchSetup;
-import io.github.thebusybiscuit.slimefun4.utils.FireworkUtils;
-import me.mrCookieSlime.Slimefun.SlimefunPlugin;
-import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
-import me.mrCookieSlime.Slimefun.api.Slimefun;
/**
- * Represents a research, which is bound to one
- * {@link SlimefunItem} or more and requires XP levels to unlock said item(s).
- *
- * @author TheBusyBiscuit
- *
- * @see ResearchSetup
- * @see ResearchUnlockEvent
+ * @deprecated Moved to io.github.thebusybiscuit.slimefun4.core.researching.Research
*
*/
-public class Research implements Keyed {
+@Deprecated
+public class Research extends io.github.thebusybiscuit.slimefun4.core.researching.Research {
- private static final int[] RESEARCH_PROGRESS = { 23, 44, 57, 92 };
-
- private final NamespacedKey key;
- private final int id;
- private String name;
- private boolean enabled = true;
- private int cost;
-
- private final List items = new LinkedList<>();
-
- /**
- * The constructor for a {@link Research}.
- *
- * Create a new research, then bind this research to the Slimefun items you want by calling
- * {@link #addItems(SlimefunItem...)}. Once you're finished, call {@link #register()}
- * to register it.
- *
- * To speed up, directly setup the research by calling
- * {@link Slimefun#registerResearch(Research, org.bukkit.inventory.ItemStack...)}.
- *
- * @param key
- * A unique identifier for this {@link Research}
- * @param id
- * old way of identifying researches
- * @param name
- * The displayed name of this {@link Research}
- * @param defaultCost
- * The Cost in XP levels to unlock this {@link Research}
- *
- */
- public Research(NamespacedKey key, int id, String name, int defaultCost) {
- this.key = key;
- this.id = id;
- this.name = name;
- this.cost = defaultCost;
- }
-
- @Override
- public NamespacedKey getKey() {
- return key;
- }
-
- /**
- * This method returns whether this {@link Research} is enabled.
- * {@code false} can mean that this particular {@link Research} was disabled or that
- * researches alltogether have been disabled.
- *
- * @return Whether this {@link Research} is enabled or not
- */
- public boolean isEnabled() {
- return SlimefunPlugin.getRegistry().isResearchingEnabled() && enabled;
- }
-
- /**
- * Gets the ID of this {@link Research}.
- * This is the old way of identifying Researches, use a {@link NamespacedKey} in the future.
- *
- * @return The ID of this {@link Research}
- */
- public int getID() {
- return id;
- }
-
- /**
- * This method gives you a localized name for this {@link Research}.
- * The name is automatically taken from the currently selected {@link Language} of
- * the specified {@link Player}.
- *
- * @param p
- * The {@link Player} to translate this name for.
- * @return The localized Name of this {@link Research}.
- */
- public String getName(Player p) {
- String localized = SlimefunPlugin.getLocal().getResearchName(p, key);
- return localized != null ? localized : name;
- }
-
- /**
- * Gets the cost in XP levels to unlock this {@link Research}.
- *
- * @return The cost in XP levels for this {@link Research}
- */
- public int getCost() {
- return cost;
- }
-
- /**
- * Sets the cost in XP levels to unlock this {@link Research}.
- *
- * @param cost
- * The cost in XP levels
- */
- public void setCost(int cost) {
- this.cost = cost;
- }
-
- /**
- * Bind the specified Slimefun items to this {@link Research}.
- *
- * @param items
- * Instances of {@link SlimefunItem} to bind to this {@link Research}
- */
- public void addItems(SlimefunItem... items) {
- for (SlimefunItem item : items) {
- if (item != null) {
- item.setResearch(this);
- }
- }
- }
-
- /**
- * Lists every {@link SlimefunItem} that is bound to this {@link Research}.
- *
- * @return The Slimefun items bound to this {@link Research}.
- */
- public List getAffectedItems() {
- return items;
- }
-
- /**
- * Checks if the {@link Player} can unlock this {@link Research}.
- *
- * @param p
- * The {@link Player} to check
- * @return Whether that {@link Player} can unlock this {@link Research}
- */
- public boolean canUnlock(Player p) {
- if (!isEnabled()) {
- return true;
- }
-
- boolean creativeResearch = p.getGameMode() == GameMode.CREATIVE && SlimefunPlugin.getRegistry().isFreeCreativeResearchingEnabled();
- return creativeResearch || p.getLevel() >= cost;
- }
-
- /**
- * Unlocks this {@link Research} for the specified {@link Player}.
- *
- * @param p
- * The {@link Player} for which to unlock this {@link Research}
- * @param instant
- * Whether to unlock the research instantly
- */
- public void unlock(Player p, boolean instant) {
- if (!instant) {
- Slimefun.runSync(() -> {
- p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F);
- SlimefunPlugin.getLocal().sendMessage(p, "messages.research.progress", true, msg -> msg.replace("%research%", getName(p)).replace("%progress%", "0%"));
- }, 10L);
- }
-
- PlayerProfile.get(p, profile -> {
- if (!profile.hasUnlocked(this)) {
- Runnable runnable = () -> {
- profile.setResearched(this, true);
- SlimefunPlugin.getLocal().sendMessage(p, "messages.unlocked", true, msg -> msg.replace("%research%", getName(p)));
-
- if (SlimefunPlugin.getRegistry().isResearchFireworkEnabled() && SlimefunGuideSettings.hasFireworksEnabled(p)) {
- FireworkUtils.launchRandom(p, 1);
- }
- };
-
- Slimefun.runSync(() -> {
- ResearchUnlockEvent event = new ResearchUnlockEvent(p, this);
- Bukkit.getPluginManager().callEvent(event);
-
- if (!event.isCancelled()) {
- if (instant) {
- runnable.run();
- }
- else if (SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().add(p.getUniqueId())) {
- SlimefunPlugin.getLocal().sendMessage(p, "messages.research.start", true, msg -> msg.replace("%research%", getName(p)));
- playResearchAnimation(p);
-
- Slimefun.runSync(() -> {
- runnable.run();
- SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().remove(p.getUniqueId());
- }, (RESEARCH_PROGRESS.length + 1) * 20L);
- }
- }
- });
- }
- });
- }
-
- private void playResearchAnimation(Player p) {
- for (int i = 1; i < RESEARCH_PROGRESS.length + 1; i++) {
- int j = i;
-
- Slimefun.runSync(() -> {
- p.playSound(p.getLocation(), Sound.ENTITY_BAT_TAKEOFF, 0.7F, 1F);
- SlimefunPlugin.getLocal().sendMessage(p, "messages.research.progress", true, msg -> msg.replace("%research%", getName(p)).replace("%progress%", RESEARCH_PROGRESS[j - 1] + "%"));
- }, i * 20L);
- }
- }
-
- /**
- * Registers this {@link Research}.
- */
- public void register() {
- SlimefunPlugin.getResearchCfg().setDefaultValue("enable-researching", true);
-
- String path = key.getNamespace() + '.' + key.getKey();
- migrate(id, path);
-
- if (SlimefunPlugin.getResearchCfg().contains(path + ".enabled") && !SlimefunPlugin.getResearchCfg().getBoolean(path + ".enabled")) {
- for (SlimefunItem item : new ArrayList<>(items)) {
- if (item != null) {
- item.setResearch(null);
- }
- }
-
- return;
- }
-
- SlimefunPlugin.getResearchCfg().setDefaultValue(path + ".cost", this.getCost());
- SlimefunPlugin.getResearchCfg().setDefaultValue(path + ".enabled", true);
-
- this.cost = SlimefunPlugin.getResearchCfg().getInt(path + ".cost");
- this.enabled = SlimefunPlugin.getResearchCfg().getBoolean(path + ".enabled");
-
- SlimefunPlugin.getRegistry().getResearches().add(this);
- SlimefunPlugin.getRegistry().getResearchIds().add(this);
- }
-
- // Temporary migration method from ids to Namespaced Keys.
- private void migrate(int id, String path) {
- if (SlimefunPlugin.getResearchCfg().contains(id + ".enabled")) {
- SlimefunPlugin.getResearchCfg().setValue(path + ".enabled", SlimefunPlugin.getResearchCfg().getBoolean(id + ".enabled"));
- }
-
- if (SlimefunPlugin.getResearchCfg().contains(id + ".cost")) {
- SlimefunPlugin.getResearchCfg().setValue(path + ".cost", SlimefunPlugin.getResearchCfg().getInt(id + ".cost"));
- }
-
- SlimefunPlugin.getResearchCfg().setValue(String.valueOf(id), null);
- }
-
- /**
- * Attempts to get a {@link Research} with the given ID.
- *
- * We will use {@link NamespacedKey} for this in the future.
- *
- * @param id
- * ID of the research to get
- * @return {@link Research} if found, or null
- */
- public static Research getByID(int id) {
- for (Research research : SlimefunPlugin.getRegistry().getResearches()) {
- if (research.getID() == id) {
- return research;
- }
- }
- return null;
- }
-
- @Override
- public String toString() {
- return "Research (" + getKey() + ')';
+ public Research(NamespacedKey key, int id, String name, int cost) {
+ super(key, id, name, cost);
}
}
\ No newline at end of file
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 de422b2cd..88bab156d 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/SlimefunItem.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/SlimefunItem.java
@@ -28,17 +28,16 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.EnergyNetComponent;
import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactive;
import io.github.thebusybiscuit.slimefun4.core.attributes.WitherProof;
import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem;
+import io.github.thebusybiscuit.slimefun4.implementation.items.backpacks.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutoDisenchanter;
import io.github.thebusybiscuit.slimefun4.implementation.items.electric.machines.AutoEnchanter;
-import io.github.thebusybiscuit.slimefun4.implementation.items.tools.SlimefunBackpack;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
import me.mrCookieSlime.Slimefun.Lists.RecipeType;
-import me.mrCookieSlime.Slimefun.Lists.SlimefunItems;
import me.mrCookieSlime.Slimefun.Objects.Category;
-import me.mrCookieSlime.Slimefun.Objects.Research;
import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler;
import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker;
import me.mrCookieSlime.Slimefun.Objects.handlers.GeneratorTicker;
@@ -129,7 +128,7 @@ public class SlimefunItem implements Placeable {
*
* @return the identifier of this {@link SlimefunItem}
*/
- public String getID() {
+ public final String getID() {
return id;
}
@@ -276,15 +275,6 @@ public class SlimefunItem implements Placeable {
}
}
- /**
- * This method returns whether this {@link SlimefunItem} was added by an addon.
- *
- * @return Whether this {@link SlimefunItem} was added by an addon.
- */
- public final boolean isAddonItem() {
- return !(addon instanceof SlimefunPlugin);
- }
-
/**
* This method returns whether this {@link SlimefunItem} is disabled.
*
@@ -457,8 +447,8 @@ public class SlimefunItem implements Placeable {
}
public void setRecipe(ItemStack[] recipe) {
- if (recipe == null || recipe.length < 9) {
- throw new IllegalArgumentException("Cannot set a recipe shorter than 9 elements.");
+ if (recipe == null || recipe.length != 9) {
+ throw new IllegalArgumentException("Recipes must be of length 9");
}
this.recipe = recipe;
@@ -539,9 +529,13 @@ public class SlimefunItem implements Placeable {
}
// Support for legacy items
- if (this instanceof ChargableItem && SlimefunUtils.isItemSimilar(item, this.item, false)) return true;
- else if (this instanceof SlimefunBackpack && SlimefunUtils.isItemSimilar(item, this.item, false)) return true;
- else return SlimefunUtils.isItemSimilar(item, this.item, true);
+ if (this instanceof ChargableItem && SlimefunUtils.isItemSimilar(item, this.item, false)) {
+ return true;
+ }
+ else {
+ boolean loreInsensitive = this instanceof SlimefunBackpack || id.equals("BROKEN_SPAWNER") || id.equals("REINFORCED_SPAWNER");
+ return SlimefunUtils.isItemSimilar(item, this.item, !loreInsensitive);
+ }
}
/**
@@ -568,6 +562,9 @@ public class SlimefunItem implements Placeable {
* Any {@link ItemHandler} that should be added to this {@link SlimefunItem}
*/
public final void addItemHandler(ItemHandler... handlers) {
+ Validate.notEmpty(handlers, "You cannot add zero handlers...");
+ Validate.noNullElements(handlers, "You cannot add any 'null' ItemHandler!");
+
if (state != ItemState.UNREGISTERED) {
throw new UnsupportedOperationException("You cannot add an ItemHandler after the SlimefunItem was registered.");
}
@@ -595,6 +592,9 @@ public class SlimefunItem implements Placeable {
* Any {@link ItemSetting} that should be added to this {@link SlimefunItem}
*/
public final void addItemSetting(ItemSetting>... settings) {
+ Validate.notEmpty(settings, "You cannot add zero settings...");
+ Validate.noNullElements(settings, "You cannot add any 'null' ItemSettings!");
+
if (state != ItemState.UNREGISTERED) {
throw new UnsupportedOperationException("You cannot add an ItemSetting after the SlimefunItem was registered.");
}
@@ -717,7 +717,12 @@ public class SlimefunItem implements Placeable {
@Override
public String toString() {
- return getClass().getSimpleName() + " - '" + id + "' (" + addon.getName() + " v" + addon.getPluginVersion() + ')';
+ if (addon == null) {
+ return getClass().getSimpleName() + " - '" + id + "'";
+ }
+ else {
+ return getClass().getSimpleName() + " - '" + id + "' (" + addon.getName() + " v" + addon.getPluginVersion() + ')';
+ }
}
@Override
@@ -794,14 +799,6 @@ public class SlimefunItem implements Placeable {
}
}
- if (SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.BROKEN_SPAWNER, false)) {
- return getByID("BROKEN_SPAWNER");
- }
-
- if (SlimefunUtils.isItemSimilar(wrapper, SlimefunItems.REPAIRED_SPAWNER, false)) {
- return getByID("REINFORCED_SPAWNER");
- }
-
return null;
}
diff --git a/src/main/java/me/mrCookieSlime/Slimefun/SlimefunPlugin.java b/src/main/java/me/mrCookieSlime/Slimefun/SlimefunPlugin.java
index dc055aede..a93d354ec 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/SlimefunPlugin.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/SlimefunPlugin.java
@@ -15,7 +15,9 @@ import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.plugin.java.JavaPluginLoader;
import io.github.thebusybiscuit.cscorelib2.config.Config;
import io.github.thebusybiscuit.cscorelib2.math.DoubleHandler;
@@ -24,10 +26,10 @@ import io.github.thebusybiscuit.cscorelib2.reflection.ReflectionUtils;
import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon;
import io.github.thebusybiscuit.slimefun4.api.gps.GPSNetwork;
-import io.github.thebusybiscuit.slimefun4.api.network.NetworkManager;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
import io.github.thebusybiscuit.slimefun4.core.SlimefunRegistry;
import io.github.thebusybiscuit.slimefun4.core.commands.SlimefunCommand;
+import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager;
import io.github.thebusybiscuit.slimefun4.core.services.AutoSavingService;
import io.github.thebusybiscuit.slimefun4.core.services.BackupService;
import io.github.thebusybiscuit.slimefun4.core.services.BlockDataService;
@@ -50,18 +52,19 @@ import io.github.thebusybiscuit.slimefun4.implementation.listeners.AncientAltarL
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BackpackListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BlockPhysicsListener;
+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.EnhancedFurnaceListener;
-import io.github.thebusybiscuit.slimefun4.implementation.listeners.MobDropListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.ExplosionsListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.FireworksListener;
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.ItemPickupListener;
+import io.github.thebusybiscuit.slimefun4.implementation.listeners.MobDropListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.MultiBlockListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.PlayerProfileListener;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.SeismicAxeListener;
@@ -103,6 +106,8 @@ import me.mrCookieSlime.Slimefun.api.inventory.UniversalBlockMenu;
public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
public static SlimefunPlugin instance;
+
+ private final boolean isTestEnvironment;
private MinecraftVersion minecraftVersion = MinecraftVersion.UNKNOWN;
private final SlimefunRegistry registry = new SlimefunRegistry();
@@ -112,9 +117,9 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
// Services - Systems that fulfill certain tasks, treat them as a black box
private final CustomItemDataService itemDataService = new CustomItemDataService(this, "slimefun_item");
private final BlockDataService blockDataService = new BlockDataService(this, "slimefun_block");
- private final CustomTextureService textureService = new CustomTextureService(this);
+ private final CustomTextureService textureService = new CustomTextureService(new Config(this, "item-models.yml"));
private final GitHubService gitHubService = new GitHubService("TheBusyBiscuit/Slimefun4");
- private final UpdaterService updaterService = new UpdaterService(this, getFile());
+ private final UpdaterService updaterService = new UpdaterService(this, getDescription().getVersion(), getFile());
private final MetricsService metricsService = new MetricsService(this);
private final AutoSavingService autoSavingService = new AutoSavingService();
private final BackupService backupService = new BackupService();
@@ -139,9 +144,24 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
private final BackpackListener backpackListener = new BackpackListener();
private final SlimefunBowListener bowListener = new SlimefunBowListener();
+ public SlimefunPlugin() {
+ super();
+ isTestEnvironment = false;
+ }
+
+ public SlimefunPlugin(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
+ super(loader, description, dataFolder, file);
+ isTestEnvironment = true;
+ }
+
@Override
public void onEnable() {
- if (getServer().getPluginManager().isPluginEnabled("CS-CoreLib")) {
+ if (isTestEnvironment) {
+ instance = this;
+ minecraftVersion = MinecraftVersion.UNIT_TEST;
+ local = new LocalizationService(this, "", null);
+ }
+ else if (getServer().getPluginManager().isPluginEnabled("CS-CoreLib")) {
long timestamp = System.nanoTime();
// We wanna ensure that the Server uses a compatible version of Minecraft
@@ -162,7 +182,15 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
// Setting up Networks
gpsNetwork = new GPSNetwork();
- networkManager = new NetworkManager(config.getInt("networks.max-size"));
+
+ int networkSize = config.getInt("networks.max-size");
+
+ if (networkSize < 1) {
+ getLogger().log(Level.WARNING, "Your 'networks.max-size' setting is misconfigured! It must be at least 1, it was set to: {0}", networkSize);
+ networkSize = 1;
+ }
+
+ networkManager = new NetworkManager(networkSize);
// Setting up bStats
metricsService.start();
@@ -194,6 +222,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
new SlimefunItemListener(this);
new SlimefunItemConsumeListener(this);
new BlockPhysicsListener(this);
+ new CargoNodeListener(this);
new MultiBlockListener(this);
new GadgetsListener(this);
new DispenserListener(this);
@@ -243,8 +272,8 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
// Initiating various Stuff and all Items with a slightly delay (0ms after the Server finished loading)
Slimefun.runSync(new SlimefunStartupTask(this, () -> {
protections = new ProtectionManager(getServer());
- textureService.register(registry.getAllSlimefunItems());
- permissionsService.register(registry.getAllSlimefunItems());
+ textureService.register(registry.getAllSlimefunItems(), true);
+ permissionsService.register(registry.getAllSlimefunItems(), true);
recipeService.refresh();
}), 0);
@@ -339,7 +368,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
@Override
public void onDisable() {
// Slimefun never loaded successfully, so we don't even bother doing stuff here
- if (instance == null) {
+ if (instance == null || isTestEnvironment) {
return;
}
diff --git a/src/main/java/me/mrCookieSlime/Slimefun/api/Slimefun.java b/src/main/java/me/mrCookieSlime/Slimefun/api/Slimefun.java
index b232a611f..40f970302 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/api/Slimefun.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/api/Slimefun.java
@@ -1,5 +1,6 @@
package me.mrCookieSlime.Slimefun.api;
+import java.util.Optional;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
@@ -29,25 +30,15 @@ public final class Slimefun {
}
/**
- * Registers this Research and automatically binds these ItemStacks to it.
- *
- * This convenience method spares from doing the code below:
+ * Registers a research.
*
- *
- * {@code
- * Research r = new Research(7, "Glowstone Armor", 3);
- * r.addItems(SlimefunItem.getByItem(SlimefunItems.GLOWSTONE_HELMET),
- * SlimefunItem.getByItem(SlimefunItems.GLOWSTONE_CHESTPLATE),
- * SlimefunItem.getByItem(SlimefunItems.GLOWSTONE_LEGGINGS),
- * SlimefunItem.getByItem(SlimefunItems.GLOWSTONE_BOOTS));
- * r.register();
- * }*
- *
+ * @deprecated The Research class was moved, this method is no longer valid. Please use
+ * {@link io.github.thebusybiscuit.slimefun4.core.researching.Research#register()} instead.
*
* @param research
- * the research to register, not null
+ * The research
* @param items
- * the items to bind, not null
+ * The items
*/
@Deprecated
public static void registerResearch(Research research, ItemStack... items) {
@@ -58,6 +49,23 @@ public final class Slimefun {
research.register();
}
+ /**
+ * Registers a research.
+ *
+ * @deprecated The Research class was moved, this method is no longer valid. Please use
+ * {@link io.github.thebusybiscuit.slimefun4.core.researching.Research#register()} instead.
+ *
+ * @param key
+ * The key
+ * @param id
+ * The id
+ * @param name
+ * The name
+ * @param cost
+ * The default cost
+ * @param items
+ * The items
+ */
@Deprecated
public static void registerResearch(NamespacedKey key, int id, String name, int cost, ItemStack... items) {
registerResearch(new Research(key, id, name, cost), items);
@@ -81,28 +89,11 @@ public final class Slimefun {
SlimefunItem sfItem = SlimefunItem.getByItem(item);
if (sfItem != null) {
- if (sfItem.getState() == ItemState.DISABLED) {
- if (message) {
- SlimefunPlugin.getLocal().sendMessage(p, "messages.disabled-item", true);
- }
-
- return false;
- }
-
- if (isEnabled(p, item, message) && hasPermission(p, sfItem, message)) {
- if (sfItem.getResearch() == null) return true;
- else if (PlayerProfile.get(p).hasUnlocked(sfItem.getResearch())) return true;
- else {
- if (message && !(sfItem instanceof VanillaItem)) {
- SlimefunPlugin.getLocal().sendMessage(p, "messages.not-researched", true);
- }
-
- return false;
- }
- }
- else return false;
+ return hasUnlocked(p, sfItem, message);
+ }
+ else {
+ return true;
}
- else return true;
}
/**
@@ -119,19 +110,33 @@ public final class Slimefun {
* false
otherwise.
*/
public static boolean hasUnlocked(Player p, SlimefunItem sfItem, boolean message) {
+ if (sfItem.getState() == ItemState.VANILLA_FALLBACK) {
+ return true;
+ }
+
if (isEnabled(p, sfItem, message) && hasPermission(p, sfItem, message)) {
if (sfItem.getResearch() == null) {
return true;
}
- else if (PlayerProfile.get(p).hasUnlocked(sfItem.getResearch())) {
- return true;
- }
else {
- if (message && !(sfItem instanceof VanillaItem)) {
- SlimefunPlugin.getLocal().sendMessage(p, "messages.not-researched", true);
- }
+ Optional profile = PlayerProfile.find(p);
- return false;
+ if (!profile.isPresent()) {
+ // We will return false since we cannot know the answer yet
+ // But we will schedule the Profile for loading.
+ PlayerProfile.request(p);
+ return false;
+ }
+ else if (profile.get().hasUnlocked(sfItem.getResearch())) {
+ return true;
+ }
+ else {
+ if (message && !(sfItem instanceof VanillaItem)) {
+ SlimefunPlugin.getLocal().sendMessage(p, "messages.not-researched", true);
+ }
+
+ return false;
+ }
}
}
diff --git a/src/main/java/me/mrCookieSlime/Slimefun/api/SlimefunItemStack.java b/src/main/java/me/mrCookieSlime/Slimefun/api/SlimefunItemStack.java
index d6ab12734..158497509 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/api/SlimefunItemStack.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/api/SlimefunItemStack.java
@@ -22,6 +22,7 @@ import org.bukkit.potion.PotionEffectType;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
import io.github.thebusybiscuit.cscorelib2.item.ImmutableItemMeta;
import io.github.thebusybiscuit.cscorelib2.skull.SkullItem;
+import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
import io.github.thebusybiscuit.slimefun4.api.exceptions.PrematureCodeException;
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
import me.mrCookieSlime.Slimefun.SlimefunPlugin;
@@ -184,6 +185,10 @@ public class SlimefunItemStack extends CustomItem {
}
private static ItemStack getSkull(String id, String texture) {
+ if (SlimefunPlugin.getMinecraftVersion() == MinecraftVersion.UNIT_TEST) {
+ return new ItemStack(Material.PLAYER_HEAD);
+ }
+
return SkullItem.fromBase64(getTexture(id, texture));
}
diff --git a/src/main/resources/languages/messages_en.yml b/src/main/resources/languages/messages_en.yml
index f40ebdecb..d13fdf0ee 100644
--- a/src/main/resources/languages/messages_en.yml
+++ b/src/main/resources/languages/messages_en.yml
@@ -14,6 +14,13 @@ commands:
description: Unlock/Reset researches for a player
reset: '&cYou have reset %player%''s Knowledge'
reset-target: '&cYour Knowledge has been reset'
+
+ backpack:
+ description: Retrieve an existing backpack
+ invalid-id: '&4The backpack id must be a non-negative number!'
+ player-never-joined: '&4No player with that name has ever joined the server!'
+ backpack-does-not-exist: '&4That backpack does not exist!'
+ restored-backpack-given: '&bBackpack restored successfully! Added to your inventory!'
guide:
locked: 'LOCKED'
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 2eb5955dc..b70199053 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -37,6 +37,9 @@ permissions:
slimefun.command.versions:
description: Allows you to do /sf versions
default: op
+ slimefun.command.backpack:
+ description: Allows you to do /sf backpack
+ default: op
slimefun.command.guide:
description: Allows you to obtain the Slimefun guide book
default: true
diff --git a/src/main/resources/wiki.json b/src/main/resources/wiki.json
index 2d8b1e456..3b598bcf9 100644
--- a/src/main/resources/wiki.json
+++ b/src/main/resources/wiki.json
@@ -1,103 +1,103 @@
{
- "GOLD_PAN": "Gold-Pan",
- "SIFTED_ORE": "Sifted-Ore",
- "SMELTERY": "Smeltery",
- "DIET_COOKIE": "Diet-Cookie",
- "ENHANCED_CRAFTING_TABLE": "Enhanced-Crafting-Table",
- "FORTUNE_COOKIE": "Fortune-Cookie",
- "TABLE_SAW": "Table-Saw",
- "APPLE_JUICE": "Juices",
- "GOLDEN_APPLE_JUICE": "Juices",
- "CARROT_JUICE": "Juices",
- "MELON_JUICE": "Juices",
- "PUMPKIN_JUICE": "Juices",
- "SWEET_BERRY_JUICE": "Juices",
- "MAGIC_SUGAR": "Magic-Sugar",
- "MONSTER_JERKY": "Monster-Jerky",
- "OUTPUT_CHEST": "Output-Chest",
- "BEEF_JERKY": "Meat-Jerky",
- "PORK_JERKY": "Meat-Jerky",
- "CHICKEN_JERKY": "Meat-Jerky",
- "MUTTON_JERKY": "Meat-Jerky",
- "RABBIT_JERKY": "Meat-Jerky",
- "FISH_JERKY": "Meat-Jerky",
- "KELP_COOKIE": "Kelp-Cookie",
- "ANCIENT_ALTAR": "Ancient-Altar",
- "ANCIENT_PEDESTAL": "Ancient-Pedestal",
- "BLADE_OF_VAMPIRES": "Blade-of-Vampires",
- "BROKEN_SPAWNER": "Broken-Spawner",
- "REPAIRED_SPAWNER": "Reinforced-Spawner",
- "NETHER_GOLD_PAN": "Nether-Gold-Pan",
- "PICKAXE_OF_CONTAINMENT": "Pickaxe-of-Containment",
- "SEISMIC_AXE": "Seismic-Axe",
- "SMELTERS_PICKAXE": "Smelter's-Pickaxe",
- "MAGNET": "Magnet",
- "BASIC_CIRCUIT_BOARD": "Circuit-Boards",
- "ADVANCED_CIRCUIT_BOARD": "Circuit-Boards",
- "BATTERY": "Battery",
- "STEEL_THRUSTER": "Steel-Thruster",
- "POWER_CRYSTAL": "Power-Crystal",
- "SOLAR_PANEL": "Solar-Panel",
- "ELECTRO_MAGNET": "Electromagnet",
- "ELECTRIC_MOTOR": "Electric-Motor",
- "HEATING_COIL": "Heating-Coil",
- "COPPER_WIRE": "Copper-Wire",
- "HARDENED_GLASS": "Hardened-Glass",
- "COOLING_UNIT": "Cooling-Unit",
- "WITHER_PROOF_OBSIDIAN": "Wither-Proof-Blocks",
- "WITHER_PROOF_GLASS": "Wither-Proof-Blocks",
- "REACTOR_COOLANT_CELL": "Coolant-Cells",
- "NETHER_ICE_COOLANT_CELL": "Coolant-Cells",
- "IRON_DUST": "Iron-Dust",
- "GOLD_DUST": "Gold-Dust",
- "GOLD_4K": "Gold-Ingot",
- "GOLD_6K": "Gold-Ingot",
- "GOLD_8K": "Gold-Ingot",
- "GOLD_10K": "Gold-Ingot",
- "GOLD_12K": "Gold-Ingot",
- "GOLD_14K": "Gold-Ingot",
- "GOLD_16K": "Gold-Ingot",
- "GOLD_18K": "Gold-Ingot",
- "GOLD_20K": "Gold-Ingot",
- "GOLD_22K": "Gold-Ingot",
- "GOLD_24K": "Gold-Ingot",
- "ENERGY_REGULATOR": "Energy-Regulator",
- "SMALL_CAPACITOR": "Energy-Capacitors",
- "MEDIUM_CAPACITOR": "Energy-Capacitors",
- "BIG_CAPACITOR": "Energy-Capacitors",
- "LARGE_CAPACITOR": "Energy-Capacitors",
- "CARBONADO_EDGED_CAPACITOR": "Energy-Capacitors",
- "SOLAR_GENERATOR": "Solar-Generator",
- "SOLAR_GENERATOR_2": "Solar-Generator",
- "SOLAR_GENERATOR_3": "Solar-Generator",
- "SOLAR_GENERATOR_4": "Solar-Generator",
- "COAL_GENERATOR": "Coal-Generator",
- "COAL_GENERATOR_2": "Coal-Generator",
- "COBALT_PICKAXE": "Cobalt-Pickaxe",
- "EXPLOSIVE_PICKAXE": "Explosive-Pickaxe",
- "EXPLOSIVE_SHOVEL": "Explosive-Shovel",
- "GRAPPLING_HOOK": "Grappling-Hook",
- "HERCULES_PICKAXE": "Hercules'-Pickaxe",
- "LUMBER_AXE": "Lumber-Axe",
- "PICKAXE_OF_VEIN_MINING": "Pickaxe-of-Vein-Mining",
- "PICKAXE_OF_THE_SEEKER": "Pickaxe-of-the-Seeker",
- "CARGO_OUTPUT_ADVANCED": "Advanced-Output-Node",
- "CARGO_MANAGER": "Cargo-Manager",
- "CARGO_MOTOR": "Cargo-Motor",
- "CARGO_NODE": "Connector-Node",
- "CARGO_INPUT": "Input-Node",
- "CARGO_OUTPUT": "Output-Node",
- "TRASH_CAN": "Trash-Can",
- "ORE_WASHER": "Ore-Washer",
- "SCUBA_HELMET": "Hazmat-Suit",
- "HAZMAT_CHESTPLATE": "Hazmat-Suit",
- "HAZMAT_LEGGINGS": "Hazmat-Suit",
- "RUBBER_BOOTS": "Hazmat-Suit",
- "ARMOR_FORGE": "Armor-Forge",
- "AUTOMATED_PANNING_MACHINE": "Automated-Panning-Machine",
- "COMPRESSOR": "Compressor",
- "ORE_CRUSHER": "Ore-Crusher",
- "PRESSURE_CHAMBER": "Pressure-Chamber",
- "GRIND_STONE": "Grind-Stone",
- "MAGIC_WORKBENCH": "Magic-Workbench"
+ "GOLD_PAN" : "Gold-Pan",
+ "SIFTED_ORE" : "Sifted-Ore",
+ "SMELTERY" : "Smeltery",
+ "DIET_COOKIE" : "Diet-Cookie",
+ "ENHANCED_CRAFTING_TABLE" : "Enhanced-Crafting-Table",
+ "FORTUNE_COOKIE" : "Fortune-Cookie",
+ "TABLE_SAW" : "Table-Saw",
+ "APPLE_JUICE" : "Juices",
+ "GOLDEN_APPLE_JUICE" : "Juices",
+ "CARROT_JUICE" : "Juices",
+ "MELON_JUICE" : "Juices",
+ "PUMPKIN_JUICE" : "Juices",
+ "SWEET_BERRY_JUICE" : "Juices",
+ "MAGIC_SUGAR" : "Magic-Sugar",
+ "MONSTER_JERKY" : "Monster-Jerky",
+ "OUTPUT_CHEST" : "Output-Chest",
+ "BEEF_JERKY" : "Meat-Jerky",
+ "PORK_JERKY" : "Meat-Jerky",
+ "CHICKEN_JERKY" : "Meat-Jerky",
+ "MUTTON_JERKY" : "Meat-Jerky",
+ "RABBIT_JERKY" : "Meat-Jerky",
+ "FISH_JERKY" : "Meat-Jerky",
+ "KELP_COOKIE" : "Kelp-Cookie",
+ "ANCIENT_ALTAR" : "Ancient-Altar",
+ "ANCIENT_PEDESTAL" : "Ancient-Pedestal",
+ "BLADE_OF_VAMPIRES" : "Blade-of-Vampires",
+ "BROKEN_SPAWNER" : "Broken-Spawner",
+ "REPAIRED_SPAWNER" : "Reinforced-Spawner",
+ "NETHER_GOLD_PAN" : "Nether-Gold-Pan",
+ "PICKAXE_OF_CONTAINMENT" : "Pickaxe-of-Containment",
+ "SEISMIC_AXE" : "Seismic-Axe",
+ "SMELTERS_PICKAXE" : "Smelter's-Pickaxe",
+ "MAGNET" : "Magnet",
+ "BASIC_CIRCUIT_BOARD" : "Circuit-Boards",
+ "ADVANCED_CIRCUIT_BOARD" : "Circuit-Boards",
+ "BATTERY" : "Battery",
+ "STEEL_THRUSTER" : "Steel-Thruster",
+ "POWER_CRYSTAL" : "Power-Crystal",
+ "SOLAR_PANEL" : "Solar-Panel",
+ "ELECTRO_MAGNET" : "Electromagnet",
+ "ELECTRIC_MOTOR" : "Electric-Motor",
+ "HEATING_COIL" : "Heating-Coil",
+ "COPPER_WIRE" : "Copper-Wire",
+ "HARDENED_GLASS" : "Hardened-Glass",
+ "COOLING_UNIT" : "Cooling-Unit",
+ "WITHER_PROOF_OBSIDIAN" : "Wither-Proof-Blocks",
+ "WITHER_PROOF_GLASS" : "Wither-Proof-Blocks",
+ "REACTOR_COOLANT_CELL" : "Coolant-Cells",
+ "NETHER_ICE_COOLANT_CELL" : "Coolant-Cells",
+ "IRON_DUST" : "Iron-Dust",
+ "GOLD_DUST" : "Gold-Dust",
+ "GOLD_4K" : "Gold-Ingot",
+ "GOLD_6K" : "Gold-Ingot",
+ "GOLD_8K" : "Gold-Ingot",
+ "GOLD_10K" : "Gold-Ingot",
+ "GOLD_12K" : "Gold-Ingot",
+ "GOLD_14K" : "Gold-Ingot",
+ "GOLD_16K" : "Gold-Ingot",
+ "GOLD_18K" : "Gold-Ingot",
+ "GOLD_20K" : "Gold-Ingot",
+ "GOLD_22K" : "Gold-Ingot",
+ "GOLD_24K" : "Gold-Ingot",
+ "ENERGY_REGULATOR" : "Energy-Regulator",
+ "SMALL_CAPACITOR" : "Energy-Capacitors",
+ "MEDIUM_CAPACITOR" : "Energy-Capacitors",
+ "BIG_CAPACITOR" : "Energy-Capacitors",
+ "LARGE_CAPACITOR" : "Energy-Capacitors",
+ "CARBONADO_EDGED_CAPACITOR" : "Energy-Capacitors",
+ "SOLAR_GENERATOR" : "Solar-Generator",
+ "SOLAR_GENERATOR_2" : "Solar-Generator",
+ "SOLAR_GENERATOR_3" : "Solar-Generator",
+ "SOLAR_GENERATOR_4" : "Solar-Generator",
+ "COAL_GENERATOR" : "Coal-Generator",
+ "COAL_GENERATOR_2" : "Coal-Generator",
+ "COBALT_PICKAXE" : "Cobalt-Pickaxe",
+ "EXPLOSIVE_PICKAXE" : "Explosive-Pickaxe",
+ "EXPLOSIVE_SHOVEL" : "Explosive-Shovel",
+ "GRAPPLING_HOOK" : "Grappling-Hook",
+ "HERCULES_PICKAXE" : "Hercules'-Pickaxe",
+ "LUMBER_AXE" : "Lumber-Axe",
+ "PICKAXE_OF_VEIN_MINING" : "Pickaxe-of-Vein-Mining",
+ "PICKAXE_OF_THE_SEEKER" : "Pickaxe-of-the-Seeker",
+ "CARGO_OUTPUT_ADVANCED" : "Advanced-Output-Node",
+ "CARGO_MANAGER" : "Cargo-Manager",
+ "CARGO_MOTOR" : "Cargo-Motor",
+ "CARGO_NODE" : "Connector-Node",
+ "CARGO_INPUT" : "Input-Node",
+ "CARGO_OUTPUT" : "Output-Node",
+ "TRASH_CAN" : "Trash-Can",
+ "ORE_WASHER" : "Ore-Washer",
+ "SCUBA_HELMET" : "Hazmat-Suit",
+ "HAZMAT_CHESTPLATE" : "Hazmat-Suit",
+ "HAZMAT_LEGGINGS" : "Hazmat-Suit",
+ "RUBBER_BOOTS" : "Hazmat-Suit",
+ "ARMOR_FORGE" : "Armor-Forge",
+ "AUTOMATED_PANNING_MACHINE" : "Automated-Panning-Machine",
+ "COMPRESSOR" : "Compressor",
+ "ORE_CRUSHER" : "Ore-Crusher",
+ "PRESSURE_CHAMBER" : "Pressure-Chamber",
+ "GRIND_STONE" : "Grind-Stone",
+ "MAGIC_WORKBENCH" : "Magic-Workbench"
}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/mocks/MockItemHandler.java b/src/test/java/io/github/thebusybiscuit/slimefun4/mocks/MockItemHandler.java
new file mode 100644
index 000000000..e5d102b02
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/mocks/MockItemHandler.java
@@ -0,0 +1,12 @@
+package io.github.thebusybiscuit.slimefun4.mocks;
+
+import me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler;
+
+public class MockItemHandler implements ItemHandler {
+
+ @Override
+ public Class extends ItemHandler> getIdentifier() {
+ return getClass();
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/mocks/MockSlimefunItem.java b/src/test/java/io/github/thebusybiscuit/slimefun4/mocks/MockSlimefunItem.java
new file mode 100644
index 000000000..41d78b58a
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/mocks/MockSlimefunItem.java
@@ -0,0 +1,14 @@
+package io.github.thebusybiscuit.slimefun4.mocks;
+
+import org.bukkit.inventory.ItemStack;
+
+import me.mrCookieSlime.Slimefun.Objects.Category;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+class MockSlimefunItem extends SlimefunItem {
+
+ public MockSlimefunItem(Category category, ItemStack item, String id) {
+ super(category, item, id, null, new ItemStack[9]);
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/mocks/TestUtilities.java b/src/test/java/io/github/thebusybiscuit/slimefun4/mocks/TestUtilities.java
new file mode 100644
index 000000000..85fed8dac
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/mocks/TestUtilities.java
@@ -0,0 +1,66 @@
+package io.github.thebusybiscuit.slimefun4.mocks;
+
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.plugin.Plugin;
+import org.junit.jupiter.api.Assertions;
+import org.mockito.Mockito;
+
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.Category;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public final class TestUtilities {
+
+ private TestUtilities() {}
+
+ public static Inventory mockInventory(InventoryType type, ItemStack... contents) {
+ Inventory inv = Mockito.mock(Inventory.class);
+
+ when(inv.getType()).thenReturn(type);
+ when(inv.getContents()).thenReturn(contents);
+
+ return inv;
+ }
+
+ public static SlimefunItem mockSlimefunItem(Plugin plugin, String id, ItemStack item) {
+ Category category = new Category(new NamespacedKey(plugin, "test"), new CustomItem(Material.EMERALD, "&4Test Category"));
+
+ return new MockSlimefunItem(category, item, id);
+ }
+
+ public static VanillaItem mockVanillaItem(Plugin plugin, Material type, boolean enabled) {
+ Category category = new Category(new NamespacedKey(plugin, "test"), new CustomItem(Material.EMERALD, "&4Test Category"));
+ VanillaItem item = new VanillaItem(category, new ItemStack(type), type.name(), null, new ItemStack[9]);
+ SlimefunPlugin.getItemCfg().setValue(type.name() + ".enabled", enabled);
+ return item;
+ }
+
+ public static PlayerProfile awaitProfile(OfflinePlayer player) throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ AtomicReference ref = new AtomicReference<>();
+
+ // This loads the profile asynchronously
+ Assertions.assertFalse(PlayerProfile.get(player, profile -> {
+ ref.set(profile);
+ latch.countDown();
+ }));
+
+ latch.await(2, TimeUnit.SECONDS);
+ return ref.get();
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/TestPluginClass.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/TestPluginClass.java
new file mode 100644
index 000000000..aaa25b594
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/TestPluginClass.java
@@ -0,0 +1,70 @@
+package io.github.thebusybiscuit.slimefun4.tests;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.slimefun4.api.MinecraftVersion;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+
+public class TestPluginClass {
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void verifyTestEnvironment() {
+ MinecraftVersion version = SlimefunPlugin.getMinecraftVersion();
+
+ Assertions.assertEquals(MinecraftVersion.UNIT_TEST, version);
+ Assertions.assertEquals("Unit Test Environment", version.getName());
+ }
+
+ @Test
+ public void testConfigs() {
+ Assertions.assertNotNull(SlimefunPlugin.getCfg());
+ Assertions.assertNotNull(SlimefunPlugin.getResearchCfg());
+ Assertions.assertNotNull(SlimefunPlugin.getItemCfg());
+ }
+
+ @Test
+ public void testGetters() {
+ Assertions.assertNotNull(SlimefunPlugin.getTicker());
+ Assertions.assertNotNull(SlimefunPlugin.getVersion());
+ Assertions.assertNotNull(SlimefunPlugin.getRegistry());
+ Assertions.assertNotNull(SlimefunPlugin.getCommand());
+ }
+
+ @Test
+ public void testServicesNotNull() {
+ Assertions.assertNotNull(SlimefunPlugin.getLocal());
+ Assertions.assertNotNull(SlimefunPlugin.getMinecraftRecipes());
+ Assertions.assertNotNull(SlimefunPlugin.getItemDataService());
+ Assertions.assertNotNull(SlimefunPlugin.getItemTextureService());
+ Assertions.assertNotNull(SlimefunPlugin.getPermissionsService());
+ Assertions.assertNotNull(SlimefunPlugin.getBlockDataService());
+ Assertions.assertNotNull(SlimefunPlugin.getThirdPartySupportService());
+ Assertions.assertNotNull(SlimefunPlugin.getWorldSettingsService());
+ Assertions.assertNotNull(SlimefunPlugin.getGitHubService());
+ Assertions.assertNotNull(SlimefunPlugin.getUpdater());
+ }
+
+ @Test
+ public void testListenersNotNull() {
+ Assertions.assertNotNull(SlimefunPlugin.getAncientAltarListener());
+ Assertions.assertNotNull(SlimefunPlugin.getGrapplingHookListener());
+ Assertions.assertNotNull(SlimefunPlugin.getBackpackListener());
+ Assertions.assertNotNull(SlimefunPlugin.getBowListener());
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestCategories.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestCategories.java
new file mode 100644
index 000000000..a033f4a69
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestCategories.java
@@ -0,0 +1,185 @@
+package io.github.thebusybiscuit.slimefun4.tests.items;
+
+import java.time.LocalDate;
+import java.time.Month;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+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.categories.LockedCategory;
+import io.github.thebusybiscuit.slimefun4.core.categories.SeasonalCategory;
+import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideLayout;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.Category;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TestCategories {
+
+ private static ServerMock server;
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testCategoryGetters() {
+ Category category = new Category(new NamespacedKey(plugin, "getter_test"), new CustomItem(Material.DIAMOND_AXE, "&6Testing"));
+
+ Assertions.assertEquals(3, category.getTier());
+ Assertions.assertEquals(new NamespacedKey(SlimefunPlugin.instance, "getter_test"), category.getKey());
+ Assertions.assertEquals("Testing", category.getUnlocalizedName());
+ Assertions.assertEquals(0, category.getItems().size());
+ }
+
+ @Test
+ public void testAddItem() {
+ Category category = new Category(new NamespacedKey(plugin, "items_test"), new CustomItem(Material.DIAMOND_AXE, "&6Testing"));
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CATEGORY_ITEMS_TEST_ITEM", new CustomItem(Material.BAMBOO, "&6Test Bamboo"));
+ item.setCategory(category);
+ item.register(plugin);
+ item.load();
+
+ Assertions.assertTrue(category.getItems().contains(item));
+ Assertions.assertEquals(1, category.getItems().size());
+
+ // Size must still be 1 since we disallow duplicates
+ item.setCategory(category);
+
+ Assertions.assertEquals(1, category.getItems().size());
+ Assertions.assertThrows(IllegalArgumentException.class, () -> category.add(null));
+ }
+
+ @Test
+ public void testHidden() {
+ Category category = new Category(new NamespacedKey(plugin, "hiddenCategory"), new ItemStack(Material.BEACON));
+ Player player = server.addPlayer();
+
+ // Empty Categories are also hidden
+ Assertions.assertTrue(category.isHidden(player));
+
+ SlimefunItem disabledItem = TestUtilities.mockSlimefunItem(plugin, "DISABLED_CATEGORY_ITEM", new CustomItem(Material.BEETROOT, "&4Disabled"));
+ SlimefunPlugin.getItemCfg().setValue("DISABLED_CATEGORY_ITEM.enabled", false);
+ disabledItem.setCategory(category);
+ disabledItem.register(plugin);
+ disabledItem.load();
+
+ // A disabled Item should also make the Category hide
+ Assertions.assertTrue(category.isHidden(player));
+
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CATEGORY_HIDDEN_TEST", new CustomItem(Material.BAMBOO, "&6Test Bamboo"));
+ item.setCategory(category);
+ item.setHidden(true);
+ item.register(plugin);
+ item.load();
+
+ // A hidden Item should also make the Category hide
+ Assertions.assertTrue(category.isHidden(player));
+
+ item.setHidden(false);
+ Assertions.assertFalse(category.isHidden(player));
+ }
+
+ @Test
+ public void testContains() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CATEGORY_TEST_ITEM_2", new CustomItem(Material.BOW, "&6Test Bow"));
+ item.register(plugin);
+ item.load();
+
+ Category category = item.getCategory();
+
+ Assertions.assertTrue(category.contains(item));
+ Assertions.assertFalse(category.contains(null));
+
+ // Unregistered Item
+ Assertions.assertFalse(category.contains(TestUtilities.mockSlimefunItem(plugin, "NULL", new ItemStack(Material.BEDROCK))));
+ }
+
+ @Test
+ public void testLockedCategories() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new LockedCategory(new NamespacedKey(plugin, "locked"), new CustomItem(Material.GOLD_NUGGET, "&6Locked Test"), (NamespacedKey) null));
+
+ Category category = new Category(new NamespacedKey(plugin, "unlocked"), new CustomItem(Material.EMERALD, "&5I am SHERlocked"));
+ category.register();
+
+ Category unregistered = new Category(new NamespacedKey(plugin, "unregistered"), new CustomItem(Material.EMERALD, "&5I am unregistered"));
+
+ LockedCategory locked = new LockedCategory(new NamespacedKey(plugin, "locked"), new CustomItem(Material.GOLD_NUGGET, "&6Locked Test"), category.getKey(), unregistered.getKey());
+ locked.register();
+
+ Assertions.assertTrue(locked.getParents().contains(category));
+ Assertions.assertFalse(locked.getParents().contains(unregistered));
+
+ locked.removeParent(category);
+ Assertions.assertFalse(locked.getParents().contains(category));
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> locked.addParent(locked));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> locked.addParent(null));
+
+ locked.addParent(category);
+ Assertions.assertTrue(locked.getParents().contains(category));
+ }
+
+ @Test
+ public void testSeasonalCategories() {
+ // Category with current Month
+ Month month = LocalDate.now().getMonth();
+ SeasonalCategory category = new SeasonalCategory(new NamespacedKey(plugin, "seasonal"), month, 1, new CustomItem(Material.NETHER_STAR, "&cSeasonal Test"));
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "SEASONAL_ITEM", new CustomItem(Material.NETHER_STAR, "&dSeasonal Test Star"));
+ item.setCategory(category);
+ item.register(plugin);
+ item.load();
+
+ Player player = server.addPlayer();
+
+ Assertions.assertEquals(month, category.getMonth());
+ Assertions.assertFalse(category.isHidden(player));
+
+ // Category with future Month
+ SeasonalCategory category2 = new SeasonalCategory(category.getKey(), month.plus(6), 1, new CustomItem(Material.MILK_BUCKET, "&dSeasonal Test"));
+ Assertions.assertTrue(category2.isHidden(player));
+ }
+
+ @Test
+ public void testFlexCategory() {
+ FlexCategory category = new FlexCategory(new NamespacedKey(plugin, "flex"), new CustomItem(Material.REDSTONE, "&4Weird flex but ok")) {
+
+ @Override
+ public void open(Player p, PlayerProfile profile, SlimefunGuideLayout layout) {
+ // Nothing
+ }
+
+ @Override
+ public boolean isVisible(Player p, PlayerProfile profile, SlimefunGuideLayout layout) {
+ return true;
+ }
+ };
+
+ Player player = server.addPlayer();
+ Assertions.assertFalse(category.isHidden(player));
+
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> category.add(null));
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> category.contains(null));
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> category.remove(null));
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> category.getItems());
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestItemHandlers.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestItemHandlers.java
new file mode 100644
index 000000000..e4e286e8d
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestItemHandlers.java
@@ -0,0 +1,102 @@
+package io.github.thebusybiscuit.slimefun4.tests.items;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.bukkit.Material;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.api.exceptions.IncompatibleItemHandlerException;
+import io.github.thebusybiscuit.slimefun4.mocks.MockItemHandler;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+import me.mrCookieSlime.Slimefun.Objects.handlers.BowShootHandler;
+import me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler;
+import me.mrCookieSlime.Slimefun.Objects.handlers.ItemUseHandler;
+
+public class TestItemHandlers {
+
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testIllegalItemHandlers() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "ITEM_HANDLER_TEST", new CustomItem(Material.DIAMOND, "&cTest"));
+ item.register(plugin);
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> item.addItemHandler());
+ Assertions.assertThrows(IllegalArgumentException.class, () -> item.addItemHandler((MockItemHandler) null));
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> item.addItemHandler(new MockItemHandler()));
+ }
+
+ @Test
+ public void testItemHandler() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "ITEM_HANDLER_TEST_2", new CustomItem(Material.DIAMOND, "&cTest"));
+
+ MockItemHandler handler = new MockItemHandler();
+ item.addItemHandler(handler);
+
+ item.register(plugin);
+
+ Assertions.assertTrue(item.getHandlers().contains(handler));
+
+ AtomicBoolean bool = new AtomicBoolean(false);
+ Assertions.assertTrue(item.callItemHandler(MockItemHandler.class, x -> bool.set(true)));
+ Assertions.assertTrue(bool.get());
+
+ AtomicBoolean bool2 = new AtomicBoolean(false);
+ Assertions.assertFalse(item.callItemHandler(ItemUseHandler.class, x -> bool2.set(true)));
+ Assertions.assertFalse(bool2.get());
+ }
+
+ @Test
+ public void testBowShootHandler() {
+ BowShootHandler handler = (e, n) -> {};
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "NOT_A_BOW", new CustomItem(Material.KELP, "&bNot a bow!"));
+
+ Optional exception = handler.validate(item);
+ Assertions.assertTrue(exception.isPresent());
+
+ SlimefunItem bow = TestUtilities.mockSlimefunItem(plugin, "A_BOW", new CustomItem(Material.BOW, "&bA bow!"));
+ Optional exception2 = handler.validate(bow);
+ Assertions.assertFalse(exception2.isPresent());
+ }
+
+ @Test
+ public void testPublicHandler() {
+ ItemHandler handler = new MockItemHandler() {
+
+ @Override
+ public boolean isPrivate() {
+ return false;
+ }
+ };
+
+ Assertions.assertFalse(handler.isPrivate());
+
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "PUBLIC_HANDLER_TEST", new CustomItem(Material.KELP, "&bHappy kelp"));
+ item.addItemHandler(handler);
+ item.register(plugin);
+
+ Map, Set> handlers = SlimefunPlugin.getRegistry().getPublicItemHandlers();
+ Assertions.assertTrue(handlers.get(handler.getIdentifier()).contains(handler));
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestItemSettings.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestItemSettings.java
new file mode 100644
index 000000000..de09e1e7c
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestItemSettings.java
@@ -0,0 +1,89 @@
+package io.github.thebusybiscuit.slimefun4.tests.items;
+
+import java.util.Optional;
+
+import org.bukkit.Material;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.api.items.ItemSetting;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TestItemSettings {
+
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testIllegalItemSettings() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "ITEM_SETTINGS_TEST", new CustomItem(Material.DIAMOND, "&cTest"));
+ item.register(plugin);
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new ItemSetting<>("prematureInvocation", "Hello world").getValue());
+ Assertions.assertThrows(IllegalArgumentException.class, () -> item.addItemSetting());
+ Assertions.assertThrows(IllegalArgumentException.class, () -> item.addItemSetting((ItemSetting) null));
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> item.addItemSetting(new ItemSetting<>("test", "Hello World")));
+ }
+
+ @Test
+ public void testAddItemSetting() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "ITEM_SETTINGS_TEST_2", new CustomItem(Material.DIAMOND, "&cTest"));
+ ItemSetting setting = new ItemSetting<>("test", "Hello World");
+
+ Assertions.assertTrue(setting.isType(String.class));
+ Assertions.assertFalse(setting.isType(Integer.class));
+
+ item.addItemSetting(setting);
+ item.register(plugin);
+
+ Assertions.assertTrue(item.getItemSettings().contains(setting));
+
+ Optional> optional = item.getItemSetting(setting.getKey(), String.class);
+ Assertions.assertTrue(optional.isPresent());
+ Assertions.assertEquals(setting, optional.get());
+ Assertions.assertEquals("Hello World", setting.getValue());
+
+ Assertions.assertFalse(item.getItemSetting(setting.getKey(), Boolean.class).isPresent());
+ Assertions.assertFalse(item.getItemSetting("I do not exist", String.class).isPresent());
+ }
+
+ @Test
+ public void testUpdateItemSetting() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "ITEM_SETTINGS_TEST_3", new CustomItem(Material.DIAMOND, "&cTest"));
+ ItemSetting setting = new ItemSetting<>("test", "Hello World");
+
+ item.addItemSetting(setting);
+ item.register(plugin);
+
+ Assertions.assertEquals("Hello World", setting.getValue());
+ setting.update("I am different");
+ Assertions.assertEquals("I am different", setting.getValue());
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> setting.update(null));
+ Assertions.assertEquals("I am different", setting.getValue());
+ }
+
+ @Test
+ public void testAlreadyExistingItemSetting() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "ITEM_SETTINGS_TEST", new CustomItem(Material.DIAMOND, "&cTest"));
+
+ item.addItemSetting(new ItemSetting<>("test", "Hello World"));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> item.addItemSetting(new ItemSetting<>("test", "Hello World")));
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestSlimefunItemRegistration.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestSlimefunItemRegistration.java
new file mode 100644
index 000000000..712b7f8f3
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/items/TestSlimefunItemRegistration.java
@@ -0,0 +1,209 @@
+package io.github.thebusybiscuit.slimefun4.tests.items;
+
+import java.util.Optional;
+
+import org.bukkit.ChatColor;
+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.Disabled;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.Category;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.ItemState;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TestSlimefunItemRegistration {
+
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testSuccessfulRegistration() {
+ String id = "TEST_ITEM";
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, id, new CustomItem(Material.DIAMOND, "&cTest"));
+
+ Assertions.assertEquals(ItemState.UNREGISTERED, item.getState());
+
+ item.register(plugin);
+
+ Assertions.assertEquals(ItemState.ENABLED, item.getState());
+ Assertions.assertFalse(item.isDisabled());
+ Assertions.assertEquals(id, item.getID());
+ Assertions.assertEquals(item, SlimefunItem.getByID(id));
+ }
+
+ @Test
+ public void testDisabledItem() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "DISABLED_ITEM", new CustomItem(Material.DIAMOND, "&cTest"));
+ SlimefunPlugin.getItemCfg().setValue("DISABLED_ITEM.enabled", false);
+ item.register(plugin);
+
+ Assertions.assertEquals(ItemState.DISABLED, item.getState());
+ Assertions.assertTrue(item.isDisabled());
+ }
+
+ @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/TheBusyBiscuit/Slimefun4/wiki/Test", wiki.get());
+ }
+
+ @Disabled("This Test provokes a ClassNotFoundException")
+ @Test
+ public void testGetItemName() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "ITEM_NAME_TEST", new CustomItem(Material.DIAMOND, "&cTest"));
+ item.register(plugin);
+
+ Assertions.assertEquals(ChatColor.RED + "Test", item.getItemName());
+ }
+
+ @Test
+ public void testVanillaItemFallback() {
+ VanillaItem item = TestUtilities.mockVanillaItem(plugin, Material.ACACIA_SIGN, false);
+ item.register(plugin);
+
+ Assertions.assertTrue(item.isUseableInWorkbench());
+ Assertions.assertEquals(ItemState.VANILLA_FALLBACK, item.getState());
+ Assertions.assertTrue(item.isDisabled());
+ }
+
+ @Test
+ public void testIdConflict() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "DUPLICATE_ID", new CustomItem(Material.DIAMOND, "&cTest"));
+ item.register(plugin);
+
+ SlimefunItem item2 = TestUtilities.mockSlimefunItem(plugin, "DUPLICATE_ID", new CustomItem(Material.DIAMOND, "&cTest"));
+ item2.register(plugin);
+
+ Assertions.assertEquals(ItemState.ENABLED, item.getState());
+ Assertions.assertEquals(ItemState.UNREGISTERED, item2.getState());
+ }
+
+ @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 testIsItem() {
+ CustomItem item = new CustomItem(Material.BEACON, "&cItem Test");
+ SlimefunItem sfItem = TestUtilities.mockSlimefunItem(plugin, "IS_ITEM_TEST", item);
+ sfItem.register(plugin);
+
+ Assertions.assertTrue(sfItem.isItem(sfItem.getItem()));
+ Assertions.assertTrue(sfItem.isItem(item));
+ Assertions.assertTrue(sfItem.isItem(new CustomItem(Material.BEACON, "&cItem Test")));
+
+ Assertions.assertFalse(sfItem.isItem(null));
+ Assertions.assertFalse(sfItem.isItem(new ItemStack(Material.BEACON)));
+ Assertions.assertFalse(sfItem.isItem(new CustomItem(Material.REDSTONE, "&cTest")));
+
+ Assertions.assertEquals(sfItem, SlimefunItem.getByItem(item));
+ }
+
+ @Test
+ public void testCategoryRegistration() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CATEGORY_TEST", new CustomItem(Material.DIAMOND, "&cTest"));
+ item.register(plugin);
+ item.load();
+
+ // null should not be a valid argument
+ Assertions.assertThrows(IllegalArgumentException.class, () -> item.setCategory(null));
+
+ Category category = item.getCategory();
+ Category category2 = new Category(new NamespacedKey(plugin, "test2"), new CustomItem(Material.OBSIDIAN, "&6Test 2"));
+
+ Assertions.assertTrue(category.contains(item));
+ Assertions.assertFalse(category2.contains(item));
+ Assertions.assertEquals(category, item.getCategory());
+
+ item.setCategory(category2);
+ Assertions.assertFalse(category.contains(item));
+ Assertions.assertTrue(category2.contains(item));
+ Assertions.assertEquals(category2, item.getCategory());
+ }
+
+ @Test
+ public void testHiddenItem() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "HIDDEN_TEST", new CustomItem(Material.DIAMOND, "&cTest"));
+ item.setHidden(true);
+ item.register(plugin);
+ item.load();
+
+ Category category = item.getCategory();
+
+ Assertions.assertTrue(item.isHidden());
+ Assertions.assertFalse(category.contains(item));
+ Assertions.assertEquals(category, item.getCategory());
+
+ item.setHidden(false);
+ Assertions.assertFalse(item.isHidden());
+ Assertions.assertTrue(category.contains(item));
+ Assertions.assertEquals(category, item.getCategory());
+
+ item.setHidden(true);
+ Assertions.assertTrue(item.isHidden());
+ Assertions.assertFalse(category.contains(item));
+ Assertions.assertEquals(category, item.getCategory());
+
+ // Do nothing if the value hasn't changed
+ item.setHidden(true);
+ Assertions.assertTrue(item.isHidden());
+ Assertions.assertFalse(category.contains(item));
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestIronGolemListener.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestIronGolemListener.java
new file mode 100644
index 000000000..db1cda9f9
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestIronGolemListener.java
@@ -0,0 +1,101 @@
+package io.github.thebusybiscuit.slimefun4.tests.listeners;
+
+import org.bukkit.Material;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.IronGolem;
+import org.bukkit.entity.Player;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+import org.bukkit.inventory.EquipmentSlot;
+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.Test;
+import org.mockito.Mockito;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem;
+import io.github.thebusybiscuit.slimefun4.implementation.listeners.IronGolemListener;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TestIronGolemListener {
+
+ private static SlimefunPlugin plugin;
+ private static IronGolemListener listener;
+ private static ServerMock server;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ listener = new IronGolemListener(plugin);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ private PlayerInteractEntityEvent callIronGolemEvent(EquipmentSlot hand, ItemStack itemInHand) {
+ // Fake Player with an Iron Ingot
+ Player player = server.addPlayer();
+
+ if (hand == EquipmentSlot.HAND) {
+ player.getInventory().setItemInMainHand(itemInHand);
+ }
+ else {
+ player.getInventory().setItemInOffHand(itemInHand);
+ }
+
+ // Fake Iron Golem
+ IronGolem golem = Mockito.mock(IronGolem.class);
+ Mockito.when(golem.getType()).thenReturn(EntityType.IRON_GOLEM);
+
+ // Fake Event
+ PlayerInteractEntityEvent event = new PlayerInteractEntityEvent(player, golem, hand);
+ listener.onIronGolemHeal(event);
+ return event;
+ }
+
+ @Test
+ public void testWithIron() {
+ // This should heal the Iron Golem
+ ItemStack item = new ItemStack(Material.IRON_INGOT);
+
+ PlayerInteractEntityEvent event = callIronGolemEvent(EquipmentSlot.HAND, item);
+ Assertions.assertFalse(event.isCancelled());
+
+ PlayerInteractEntityEvent event2 = callIronGolemEvent(EquipmentSlot.OFF_HAND, item);
+ Assertions.assertFalse(event2.isCancelled());
+ }
+
+ @Test
+ public void testWithSlimefunIron() {
+ SlimefunItem slimefunItem = TestUtilities.mockSlimefunItem(plugin, "SLIMEFUN_IRON", new CustomItem(Material.IRON_INGOT, "&cSlimefun Iron"));
+ slimefunItem.register(plugin);
+
+ // The Event should be cancelled, we do not wanna use Slimefun Items for this
+ PlayerInteractEntityEvent event = callIronGolemEvent(EquipmentSlot.HAND, slimefunItem.getItem());
+ Assertions.assertTrue(event.isCancelled());
+
+ PlayerInteractEntityEvent event2 = callIronGolemEvent(EquipmentSlot.OFF_HAND, slimefunItem.getItem());
+ Assertions.assertTrue(event2.isCancelled());
+ }
+
+ @Test
+ public void testWithVanillaIron() {
+ VanillaItem item = TestUtilities.mockVanillaItem(plugin, Material.IRON_INGOT, true);
+ item.register(plugin);
+
+ PlayerInteractEntityEvent event = callIronGolemEvent(EquipmentSlot.HAND, item.getItem());
+ Assertions.assertFalse(event.isCancelled());
+
+ PlayerInteractEntityEvent event2 = callIronGolemEvent(EquipmentSlot.OFF_HAND, item.getItem());
+ Assertions.assertFalse(event2.isCancelled());
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestNetworkListener.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestNetworkListener.java
new file mode 100644
index 000000000..610f1c218
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestNetworkListener.java
@@ -0,0 +1,70 @@
+package io.github.thebusybiscuit.slimefun4.tests.listeners;
+
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.BlockState;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.inventory.EquipmentSlot;
+import org.bukkit.inventory.ItemStack;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import io.github.thebusybiscuit.slimefun4.api.network.Network;
+import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager;
+import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+
+public class TestNetworkListener {
+
+ private static SlimefunPlugin plugin;
+ private static NetworkListener listener;
+ private static NetworkManager manager = new NetworkManager(80);
+ private static ServerMock server;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ listener = new NetworkListener(plugin, manager);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testBlockBreak() {
+ World world = server.addSimpleWorld("Simple Network Listener World");
+ Location l = new Location(world, 3000, 120, -500);
+
+ Network network = Mockito.mock(Network.class);
+ Mockito.when(network.connectsTo(l)).thenReturn(true);
+ manager.registerNetwork(network);
+
+ listener.onBlockBreak(new BlockBreakEvent(l.getBlock(), server.addPlayer()));
+ Mockito.verify(network).markDirty(l);
+ }
+
+ @Test
+ public void testBlockPlace() {
+ World world = server.addSimpleWorld("Simple Network Listener World");
+ Location l = new Location(world, 3000, 120, -500);
+ Location l2 = new Location(world, 3000, 121, -500);
+
+ Network network = Mockito.mock(Network.class);
+ Mockito.when(network.connectsTo(l)).thenReturn(true);
+ manager.registerNetwork(network);
+
+ BlockState state = Mockito.mock(BlockState.class);
+ listener.onBlockPlace(new BlockPlaceEvent(l.getBlock(), state, l2.getBlock(), new ItemStack(Material.AIR), server.addPlayer(), true, EquipmentSlot.HAND));
+ Mockito.verify(network).markDirty(l);
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestVanillaMachinesListener.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestVanillaMachinesListener.java
new file mode 100644
index 000000000..dd99d5d09
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/listeners/TestVanillaMachinesListener.java
@@ -0,0 +1,262 @@
+package io.github.thebusybiscuit.slimefun4.tests.listeners;
+
+import org.apache.commons.lang.mutable.MutableObject;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Event.Result;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.event.inventory.CraftItemEvent;
+import org.bukkit.event.inventory.InventoryAction;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.event.inventory.InventoryType.SlotType;
+import org.bukkit.event.inventory.PrepareItemCraftEvent;
+import org.bukkit.inventory.CraftingInventory;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryView;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.Recipe;
+import org.bukkit.inventory.ShapedRecipe;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide;
+import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideLayout;
+import io.github.thebusybiscuit.slimefun4.implementation.items.VanillaItem;
+import io.github.thebusybiscuit.slimefun4.implementation.listeners.VanillaMachinesListener;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TestVanillaMachinesListener {
+
+ private static SlimefunPlugin plugin;
+ private static VanillaMachinesListener listener;
+ private static ServerMock server;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ listener = new VanillaMachinesListener(plugin);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ private InventoryClickEvent mockGrindStoneEvent(ItemStack item) {
+ Player player = server.addPlayer();
+ Inventory inv = TestUtilities.mockInventory(InventoryType.GRINDSTONE, item, null);
+ InventoryView view = player.openInventory(inv);
+ InventoryClickEvent event = new InventoryClickEvent(view, SlotType.CONTAINER, 2, ClickType.LEFT, InventoryAction.PICKUP_ONE);
+
+ listener.onGrindstone(event);
+ return event;
+ }
+
+ private InventoryClickEvent mockAnvilEvent(ItemStack item) {
+ Player player = server.addPlayer();
+ Inventory inv = TestUtilities.mockInventory(InventoryType.ANVIL, item, null, new ItemStack(Material.IRON_CHESTPLATE));
+ InventoryView view = player.openInventory(inv);
+ InventoryClickEvent event = new InventoryClickEvent(view, SlotType.CONTAINER, 2, ClickType.LEFT, InventoryAction.PICKUP_ONE);
+
+ listener.onAnvil(event);
+ return event;
+ }
+
+ private InventoryClickEvent mockBrewingEvent(ItemStack item) {
+ Player player = server.addPlayer();
+ Inventory inv = TestUtilities.mockInventory(InventoryType.BREWING);
+ InventoryView view = player.openInventory(inv);
+ view.setCursor(item);
+ InventoryClickEvent event = new InventoryClickEvent(view, SlotType.CONTAINER, 1, ClickType.LEFT, InventoryAction.PICKUP_ONE);
+
+ listener.onPreBrew(event);
+ return event;
+ }
+
+ private CraftItemEvent mockCraftingEvent(ItemStack item) {
+ Recipe recipe = new ShapedRecipe(new NamespacedKey(plugin, "test_recipe"), new ItemStack(Material.EMERALD));
+ Player player = server.addPlayer();
+
+ CraftingInventory inv = Mockito.mock(CraftingInventory.class);
+ Mockito.when(inv.getContents()).thenReturn(new ItemStack[] { item, null, null, null, null, null, null, null, null });
+
+ InventoryView view = player.openInventory(inv);
+ CraftItemEvent event = new CraftItemEvent(recipe, view, SlotType.RESULT, 9, ClickType.LEFT, InventoryAction.PICKUP_ALL);
+
+ listener.onCraft(event);
+ return event;
+ }
+
+ private PrepareItemCraftEvent mockPreCraftingEvent(ItemStack item) {
+ Player player = server.addPlayer();
+
+ CraftingInventory inv = Mockito.mock(CraftingInventory.class);
+ MutableObject result = new MutableObject(new ItemStack(Material.EMERALD));
+
+ Mockito.doAnswer(invocation -> {
+ ItemStack argument = invocation.getArgument(0);
+ result.setValue(argument);
+ return null;
+ }).when(inv).setResult(Mockito.any());
+
+ Mockito.when(inv.getResult()).thenAnswer(invocation -> result.getValue());
+ Mockito.when(inv.getContents()).thenReturn(new ItemStack[] { null, null, item, null, null, null, null, null, null });
+
+ InventoryView view = player.openInventory(inv);
+ PrepareItemCraftEvent event = new PrepareItemCraftEvent(inv, view, false);
+
+ listener.onPrepareCraft(event);
+ return event;
+ }
+
+ @Test
+ public void testGrindStoneWithoutSlimefunItems() {
+ InventoryClickEvent event = mockGrindStoneEvent(new ItemStack(Material.ENCHANTED_BOOK));
+ Assertions.assertEquals(Result.DEFAULT, event.getResult());
+ }
+
+ @Test
+ public void testGrindStoneWithSlimefunItem() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "ENCHANTED_MOCK_BOOK", new CustomItem(Material.ENCHANTED_BOOK, "&6Mock"));
+ item.register(plugin);
+
+ InventoryClickEvent event = mockGrindStoneEvent(item.getItem());
+ Assertions.assertEquals(Result.DENY, event.getResult());
+ }
+
+ @Test
+ public void testGrindStoneWithVanillaItem() {
+ VanillaItem item = TestUtilities.mockVanillaItem(plugin, Material.ENCHANTED_BOOK, true);
+ item.register(plugin);
+
+ InventoryClickEvent event = mockGrindStoneEvent(item.getItem());
+ Assertions.assertEquals(Result.DEFAULT, event.getResult());
+ }
+
+ @Test
+ public void testGrindStoneWithSlimefunGuide() {
+ InventoryClickEvent event = mockGrindStoneEvent(SlimefunGuide.getItem(SlimefunGuideLayout.CHEST));
+ Assertions.assertEquals(Result.DENY, event.getResult());
+ }
+
+ @Test
+ public void testCraftEventWithoutSlimefunItems() {
+ CraftItemEvent event = mockCraftingEvent(new ItemStack(Material.DIAMOND));
+ Assertions.assertEquals(Result.DEFAULT, event.getResult());
+ }
+
+ @Test
+ public void testCraftEventWithSlimefunItem() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "MOCK_DIAMOND", new CustomItem(Material.DIAMOND, "&cMock Diamond"));
+ item.register(plugin);
+
+ CraftItemEvent event = mockCraftingEvent(item.getItem());
+ Assertions.assertEquals(Result.DENY, event.getResult());
+ }
+
+ @Test
+ public void testCraftEventWithChangingSlimefunItem() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "CHANGING_ITEM", new CustomItem(Material.DIAMOND, "&dChanging Diamond"));
+ item.register(plugin);
+
+ item.setUseableInWorkbench(true);
+ CraftItemEvent event = mockCraftingEvent(item.getItem());
+ Assertions.assertEquals(Result.DEFAULT, event.getResult());
+
+ item.setUseableInWorkbench(false);
+ CraftItemEvent event2 = mockCraftingEvent(item.getItem());
+ Assertions.assertEquals(Result.DENY, event2.getResult());
+ }
+
+ @Test
+ public void testCraftEventWithVanillaItem() {
+ VanillaItem item = TestUtilities.mockVanillaItem(plugin, Material.DIAMOND, true);
+ item.register(plugin);
+
+ CraftItemEvent event = mockCraftingEvent(item.getItem());
+ Assertions.assertEquals(Result.DEFAULT, event.getResult());
+ }
+
+ @Test
+ public void testPreCraftEventWithoutSlimefunItems() {
+ PrepareItemCraftEvent event = mockPreCraftingEvent(new ItemStack(Material.DIAMOND));
+ Assertions.assertNotNull(event.getInventory().getResult());
+ }
+
+ @Test
+ public void testPreCraftEventWithSlimefunItem() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "MOCK_DIAMOND2", new CustomItem(Material.DIAMOND, "&cMock Diamond"));
+ item.register(plugin);
+
+ PrepareItemCraftEvent event = mockPreCraftingEvent(item.getItem());
+ Assertions.assertNull(event.getInventory().getResult());
+ }
+
+ @Test
+ public void testPreCraftEventWithVanillaItem() {
+ VanillaItem item = TestUtilities.mockVanillaItem(plugin, Material.GOLD_INGOT, true);
+ item.register(plugin);
+
+ PrepareItemCraftEvent event = mockPreCraftingEvent(item.getItem());
+ Assertions.assertNotNull(event.getInventory().getResult());
+ }
+
+ @Test
+ public void testAnvilWithoutSlimefunItems() {
+ InventoryClickEvent event = mockAnvilEvent(new ItemStack(Material.IRON_SWORD));
+ Assertions.assertEquals(Result.DEFAULT, event.getResult());
+ }
+
+ @Test
+ public void testAnvilWithSlimefunItem() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "MOCKED_IRON_SWORD", new CustomItem(Material.IRON_SWORD, "&6Mock"));
+ item.register(plugin);
+
+ InventoryClickEvent event = mockAnvilEvent(item.getItem());
+ Assertions.assertEquals(Result.DENY, event.getResult());
+ }
+
+ @Test
+ public void testAnvilWithVanillaItem() {
+ VanillaItem item = TestUtilities.mockVanillaItem(plugin, Material.IRON_SWORD, true);
+ item.register(plugin);
+
+ InventoryClickEvent event = mockAnvilEvent(item.getItem());
+ Assertions.assertEquals(Result.DEFAULT, event.getResult());
+ }
+
+ @Test
+ public void testBrewingWithoutSlimefunItems() {
+ InventoryClickEvent event = mockBrewingEvent(new ItemStack(Material.BLAZE_POWDER));
+ Assertions.assertEquals(Result.DEFAULT, event.getResult());
+ }
+
+ @Test
+ public void testBrewingWithSlimefunItem() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "MOCK_POWDER", new CustomItem(Material.BLAZE_POWDER, "&6Mock"));
+ item.register(plugin);
+
+ InventoryClickEvent event = mockBrewingEvent(item.getItem());
+ Assertions.assertEquals(Result.DENY, event.getResult());
+ }
+
+ @Test
+ public void testBrewingithVanillaItem() {
+ VanillaItem item = TestUtilities.mockVanillaItem(plugin, Material.BLAZE_POWDER, true);
+ item.register(plugin);
+
+ InventoryClickEvent event = mockBrewingEvent(item.getItem());
+ Assertions.assertEquals(Result.DEFAULT, event.getResult());
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/multiblocks/TestMultiBlocks.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/multiblocks/TestMultiBlocks.java
new file mode 100644
index 000000000..ac12be940
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/multiblocks/TestMultiBlocks.java
@@ -0,0 +1,90 @@
+package io.github.thebusybiscuit.slimefun4.tests.multiblocks;
+
+import org.bukkit.Material;
+import org.bukkit.block.BlockFace;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.core.MultiBlock;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TestMultiBlocks {
+
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testInvalidConstructors() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "MULTIBLOCK_TEST", new CustomItem(Material.BRICK, "&5Multiblock Test"));
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new MultiBlock(null, null, null));
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new MultiBlock(item, null, null));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new MultiBlock(item, new Material[4], null));
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new MultiBlock(item, new Material[9], BlockFace.EAST_NORTH_EAST));
+ }
+
+ @Test
+ public void testValidConstructor() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "MULTIBLOCK_TEST", new CustomItem(Material.BRICK, "&5Multiblock Test"));
+ MultiBlock multiblock = new MultiBlock(item, new Material[9], BlockFace.DOWN);
+
+ Assertions.assertEquals(item, multiblock.getSlimefunItem());
+ Assertions.assertArrayEquals(new Material[9], multiblock.getStructure());
+ Assertions.assertEquals(BlockFace.DOWN, multiblock.getTriggerBlock());
+ }
+
+ @Test
+ public void testSymmetry() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "MULTIBLOCK_TEST", new CustomItem(Material.BRICK, "&5Multiblock Test"));
+
+ MultiBlock multiblock = new MultiBlock(item, new Material[] { null, null, null, Material.DIAMOND_BLOCK, null, Material.DIAMOND_BLOCK, null, Material.DISPENSER, null }, BlockFace.DOWN);
+
+ Assertions.assertTrue(multiblock.isSymmetric());
+
+ MultiBlock multiblock2 = new MultiBlock(item, new Material[] { Material.EMERALD_BLOCK, null, null, Material.EMERALD_BLOCK, null, Material.DIAMOND_BLOCK, Material.EMERALD_BLOCK, Material.DISPENSER, null }, BlockFace.DOWN);
+
+ Assertions.assertFalse(multiblock2.isSymmetric());
+ }
+
+ @Test
+ public void testEquality() {
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "MULTIBLOCK_TEST", new CustomItem(Material.BRICK, "&5Multiblock Test"));
+
+ MultiBlock multiblock = new MultiBlock(item, new Material[] { Material.BIRCH_WOOD, Material.BIRCH_WOOD, Material.BIRCH_WOOD, null, Material.CRAFTING_TABLE, null, Material.BIRCH_WOOD, Material.DISPENSER, Material.BIRCH_WOOD }, BlockFace.DOWN);
+
+ MultiBlock multiblock2 = new MultiBlock(item, new Material[] { Material.BIRCH_WOOD, Material.BIRCH_WOOD, Material.BIRCH_WOOD, null, Material.CRAFTING_TABLE, null, Material.BIRCH_WOOD, Material.DISPENSER, Material.BIRCH_WOOD }, BlockFace.DOWN);
+
+ MultiBlock multiblock3 = new MultiBlock(item, new Material[] { Material.BIRCH_WOOD, Material.BIRCH_WOOD, Material.BIRCH_WOOD, null, Material.EMERALD_BLOCK, null, Material.BIRCH_WOOD, Material.DISPENSER, Material.BIRCH_WOOD }, BlockFace.DOWN);
+
+ MultiBlock multiblock4 = new MultiBlock(item, new Material[] { Material.DROPPER, Material.BIRCH_WOOD, Material.BIRCH_WOOD, null, Material.DIAMOND_BLOCK, null, Material.BIRCH_WOOD, Material.DISPENSER, Material.TNT }, BlockFace.DOWN);
+
+ MultiBlock multiblock5 = new MultiBlock(item, new Material[] { Material.BIRCH_WOOD, Material.BIRCH_WOOD, Material.BIRCH_WOOD, null, Material.CRAFTING_TABLE, null, Material.BIRCH_WOOD, Material.DISPENSER, Material.BIRCH_WOOD }, BlockFace.SELF);
+
+ Assertions.assertTrue(multiblock.isSymmetric());
+
+ Assertions.assertTrue(multiblock.equals(multiblock2));
+ Assertions.assertFalse(multiblock.equals(null));
+ Assertions.assertFalse(multiblock.equals(multiblock3));
+ Assertions.assertFalse(multiblock.equals(multiblock4));
+ Assertions.assertFalse(multiblock.equals(multiblock5));
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/networks/TestNetworkManager.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/networks/TestNetworkManager.java
new file mode 100644
index 000000000..787444ffc
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/networks/TestNetworkManager.java
@@ -0,0 +1,175 @@
+package io.github.thebusybiscuit.slimefun4.tests.networks;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import io.github.thebusybiscuit.slimefun4.api.network.Network;
+import io.github.thebusybiscuit.slimefun4.api.network.NetworkComponent;
+import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager;
+import io.github.thebusybiscuit.slimefun4.core.networks.cargo.CargoNet;
+
+public class TestNetworkManager {
+
+ private static ServerMock server;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testIllegalNetworkSize() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new NetworkManager(-100));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new NetworkManager(0));
+ }
+
+ @Test
+ public void testGetMaxNetworkSize() {
+ int size = 50;
+ NetworkManager manager = new NetworkManager(size);
+
+ Assertions.assertEquals(size, manager.getMaxSize());
+ }
+
+ @Test
+ public void testGetNetworkList() {
+ NetworkManager manager = new NetworkManager(10);
+ World world = server.addSimpleWorld("Simple Network World");
+ Location loc = new Location(world, 0, 100, 0);
+
+ Network network = new DummyNetwork(manager, loc, 10, new HashMap<>());
+
+ Assertions.assertFalse(manager.getNetworkList().contains(network));
+ manager.registerNetwork(network);
+ Assertions.assertTrue(manager.getNetworkList().contains(network));
+ manager.unregisterNetwork(network);
+ Assertions.assertFalse(manager.getNetworkList().contains(network));
+ }
+
+ @Test
+ public void testDirtyRegulatorUnregistersNetwork() {
+ World world = server.addSimpleWorld("Simple Network World");
+ Location loc = new Location(world, 0, 100, 0);
+
+ NetworkManager manager = Mockito.mock(NetworkManager.class);
+ Network network = new DummyNetwork(manager, loc, 10, new HashMap<>());
+ network.markDirty(loc);
+
+ Mockito.verify(manager).unregisterNetwork(network);
+ }
+
+ @Test
+ public void testGetNetworkAtLocation() {
+ 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);
+
+ Network network = new DummyNetwork(manager, loc, 10, new HashMap<>());
+
+ Assertions.assertFalse(manager.getNetworkFromLocation(loc, DummyNetwork.class).isPresent());
+
+ manager.registerNetwork(network);
+
+ Optional optional = manager.getNetworkFromLocation(loc, DummyNetwork.class);
+ Assertions.assertTrue(optional.isPresent());
+ Assertions.assertEquals(network, optional.get());
+
+ Assertions.assertFalse(manager.getNetworkFromLocation(loc2, DummyNetwork.class).isPresent());
+ Assertions.assertFalse(manager.getNetworkFromLocation(loc, CargoNet.class).isPresent());
+ }
+
+ @Test
+ public void testGetNetworksAtLocation() {
+ 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);
+
+ Network network = new DummyNetwork(manager, loc, 10, new HashMap<>());
+ manager.registerNetwork(network);
+
+ Assertions.assertFalse(manager.getNetworksFromLocation(loc2, DummyNetwork.class).contains(network));
+ Assertions.assertFalse(manager.getNetworksFromLocation(loc, CargoNet.class).contains(network));
+ Assertions.assertTrue(manager.getNetworksFromLocation(loc, DummyNetwork.class).contains(network));
+ }
+
+ @Test
+ public void testSingleNodeNetwork() {
+ NetworkManager manager = new NetworkManager(1);
+ World world = server.addSimpleWorld("Simple Network World");
+ Location loc = new Location(world, 0, 100, 0);
+
+ Network network = new DummyNetwork(manager, loc, 1, new HashMap<>());
+ network.tick();
+
+ Assertions.assertEquals(1, network.getSize());
+ Assertions.assertEquals(NetworkComponent.REGULATOR, network.classifyLocation(loc));
+ }
+
+ @Test
+ public void testCornerConnection() {
+ NetworkManager manager = new NetworkManager(100);
+ World world = server.addSimpleWorld("Simple Network World");
+ Map map = new HashMap<>();
+
+ Location loc = new Location(world, 0, 100, 0);
+
+ Location loc2 = new Location(world, 0, 100, 2);
+ map.put(loc2, NetworkComponent.CONNECTOR);
+
+ Location loc3 = new Location(world, 2, 100, 2);
+ map.put(loc3, NetworkComponent.CONNECTOR);
+
+ Network network = new DummyNetwork(manager, loc, 3, map);
+ network.tick();
+
+ Assertions.assertEquals(3, network.getSize());
+ }
+
+ private class DummyNetwork extends Network {
+
+ private final int range;
+ private final Map locations;
+
+ protected DummyNetwork(NetworkManager manager, Location regulator, int range, Map locations) {
+ super(manager, regulator);
+ this.range = range;
+ this.locations = locations;
+ }
+
+ @Override
+ public int getRange() {
+ return range;
+ }
+
+ @Override
+ public NetworkComponent classifyLocation(Location l) {
+ if (l.equals(regulator)) return NetworkComponent.REGULATOR;
+ return locations.get(l);
+ }
+
+ @Override
+ public void onClassificationChange(Location l, NetworkComponent from, NetworkComponent to) {
+ // Do nothing
+ }
+
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/profiles/TestGuideHistory.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/profiles/TestGuideHistory.java
new file mode 100644
index 000000000..81e7f7b4d
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/profiles/TestGuideHistory.java
@@ -0,0 +1,129 @@
+package io.github.thebusybiscuit.slimefun4.tests.profiles;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.core.guide.GuideHistory;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.Category;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TestGuideHistory {
+
+ private static ServerMock server;
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testDefaults() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ Assertions.assertNotNull(profile.getGuideHistory());
+ Assertions.assertEquals(0, profile.getGuideHistory().size());
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> new GuideHistory(null));
+ }
+
+ @Test
+ public void testSearchTerm() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+ GuideHistory history = profile.getGuideHistory();
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> history.add((String) null));
+
+ Assertions.assertEquals(0, history.size());
+ history.add("Walshrus");
+ Assertions.assertEquals(1, history.size());
+ }
+
+ @Test
+ public void testClear() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+ GuideHistory history = profile.getGuideHistory();
+
+ Assertions.assertEquals(0, history.size());
+ history.add("Walshrus");
+ Assertions.assertEquals(1, history.size());
+ history.clear();
+ Assertions.assertEquals(0, history.size());
+ }
+
+ @Test
+ public void testSlimefunItem() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+ GuideHistory history = profile.getGuideHistory();
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> history.add((SlimefunItem) null));
+
+ Assertions.assertEquals(0, history.size());
+
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "HISTORIC_ITEM", new CustomItem(Material.DIORITE, "&4I am really running out of ideas for item names"));
+ history.add(item);
+
+ Assertions.assertEquals(1, history.size());
+ }
+
+ @Test
+ public void testItem() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+ GuideHistory history = profile.getGuideHistory();
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> history.add((ItemStack) null, 1));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> history.add(new ItemStack(Material.DIAMOND), -20));
+
+ Assertions.assertEquals(0, history.size());
+ history.add(new ItemStack(Material.REDSTONE), 1);
+ Assertions.assertEquals(1, history.size());
+
+ // This should not add a new entry but rather only update the page
+ history.add(new ItemStack(Material.REDSTONE), 2);
+ Assertions.assertEquals(1, history.size());
+ }
+
+ @Test
+ public void testCategory() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+ GuideHistory history = profile.getGuideHistory();
+
+ Category category = new Category(new NamespacedKey(plugin, "category_guide_history"), new CustomItem(Material.BEDROCK, "&4Can't touch this"));
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> history.add((Category) null, 1));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> history.add(category, -20));
+
+ Assertions.assertEquals(0, history.size());
+ history.add(category, 1);
+ Assertions.assertEquals(1, history.size());
+
+ // This should not add a new entry but rather only update the page
+ history.add(category, 2);
+ Assertions.assertEquals(1, history.size());
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/profiles/TestPlayerBackpacks.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/profiles/TestPlayerBackpacks.java
new file mode 100644
index 000000000..d514c66ca
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/profiles/TestPlayerBackpacks.java
@@ -0,0 +1,114 @@
+package io.github.thebusybiscuit.slimefun4.tests.profiles;
+
+import java.util.Optional;
+
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack;
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+
+public class TestPlayerBackpacks {
+
+ private static ServerMock server;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testCreateBackpack() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+ Assertions.assertFalse(profile.isDirty());
+
+ PlayerBackpack backpack = profile.createBackpack(18);
+
+ Assertions.assertNotNull(backpack);
+
+ // Creating a backpack should mark profiles as dirty
+ Assertions.assertTrue(profile.isDirty());
+
+ Assertions.assertEquals(profile, backpack.getOwner());
+ Assertions.assertEquals(18, backpack.getSize());
+ Assertions.assertEquals(18, backpack.getInventory().getSize());
+ }
+
+ @Test
+ public void testChangeSize() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+ PlayerBackpack backpack = profile.createBackpack(9);
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> profile.createBackpack(-9));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> profile.createBackpack(12));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> profile.createBackpack(3000));
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> backpack.setSize(-9));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> backpack.setSize(12));
+ Assertions.assertThrows(IllegalArgumentException.class, () -> backpack.setSize(3000));
+
+ backpack.setSize(27);
+
+ Assertions.assertEquals(27, backpack.getSize());
+ Assertions.assertTrue(profile.isDirty());
+ }
+
+ @Test
+ public void testGetBackpackById() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+ PlayerBackpack backpack = profile.createBackpack(9);
+ int id = backpack.getId();
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> profile.getBackpack(-20));
+
+ Optional optional = profile.getBackpack(id);
+ Assertions.assertTrue(optional.isPresent());
+ Assertions.assertEquals(backpack, optional.get());
+
+ Assertions.assertFalse(profile.getBackpack(500).isPresent());
+ }
+
+ @Test
+ public void testLoadBackpackFromFile() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ profile.getConfig().setValue("backpacks.50.size", 27);
+
+ for (int i = 0; i < 27; i++) {
+ profile.getConfig().setValue("backpacks.50.contents." + i, new ItemStack(Material.DIAMOND));
+ }
+
+ Optional optional = profile.getBackpack(50);
+ Assertions.assertTrue(optional.isPresent());
+
+ PlayerBackpack backpack = optional.get();
+ Assertions.assertEquals(50, backpack.getId());
+ Assertions.assertEquals(27, backpack.getSize());
+ Assertions.assertEquals(-1, backpack.getInventory().firstEmpty());
+
+ backpack.getInventory().setItem(1, new ItemStack(Material.NETHER_STAR));
+
+ Assertions.assertEquals(new ItemStack(Material.DIAMOND), profile.getConfig().getItem("backpacks.50.contents.1"));
+
+ // Saving should write it to the Config file
+ backpack.save();
+ Assertions.assertEquals(new ItemStack(Material.NETHER_STAR), profile.getConfig().getItem("backpacks.50.contents.1"));
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/profiles/TestPlayerProfile.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/profiles/TestPlayerProfile.java
new file mode 100644
index 000000000..fa675ac78
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/profiles/TestPlayerProfile.java
@@ -0,0 +1,126 @@
+package io.github.thebusybiscuit.slimefun4.tests.profiles;
+
+import java.util.Iterator;
+import java.util.Optional;
+
+import org.bukkit.OfflinePlayer;
+import org.bukkit.entity.Player;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import be.seeseemelk.mockbukkit.entity.OfflinePlayerMock;
+import io.github.thebusybiscuit.slimefun4.api.items.HashedArmorpiece;
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+
+public class TestPlayerProfile {
+
+ private static ServerMock server;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testOfflinePlayer() throws InterruptedException {
+ OfflinePlayer player = new OfflinePlayerMock("Offline Test Player");
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ Assertions.assertNotNull(profile);
+ Assertions.assertEquals(profile, SlimefunPlugin.getRegistry().getPlayerProfiles().get(player.getUniqueId()));
+
+ // This profile should now be in memory and return true
+ Assertions.assertTrue(PlayerProfile.get(player, p -> {}));
+ Assertions.assertTrue(PlayerProfile.request(player));
+
+ // The Player is offline so this should return null
+ Assertions.assertNull(profile.getPlayer());
+
+ Optional optional = PlayerProfile.find(player);
+ Assertions.assertTrue(optional.isPresent());
+ Assertions.assertEquals(profile, optional.get());
+ }
+
+ @Test
+ public void testOnlinePlayer() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ Assertions.assertNotNull(profile);
+ Assertions.assertEquals(player, profile.getPlayer());
+ }
+
+ @Test
+ public void testIterator() throws InterruptedException {
+ // Clear out any previous profiles
+ Iterator clear = PlayerProfile.iterator();
+ while (clear.hasNext()) {
+ clear.next();
+ clear.remove();
+ }
+
+ Iterator emptyIterator = PlayerProfile.iterator();
+ Assertions.assertFalse(emptyIterator.hasNext());
+
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ Iterator iterator = PlayerProfile.iterator();
+ Assertions.assertTrue(iterator.hasNext());
+ Assertions.assertEquals(profile, iterator.next());
+ }
+
+ @Test
+ public void testAttributes() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ Assertions.assertNotNull(profile.getConfig());
+
+ Assertions.assertFalse(profile.isDirty());
+ profile.markDirty();
+ Assertions.assertTrue(profile.isDirty());
+
+ Assertions.assertFalse(profile.isMarkedForDeletion());
+ profile.markForDeletion();
+ Assertions.assertTrue(profile.isMarkedForDeletion());
+
+ HashedArmorpiece[] armor = profile.getArmor();
+ Assertions.assertEquals(4, armor.length);
+
+ Assertions.assertNotNull(armor[0]);
+ Assertions.assertNotNull(armor[1]);
+ Assertions.assertNotNull(armor[2]);
+ Assertions.assertNotNull(armor[3]);
+ }
+
+ @Test
+ public void testNotExistentProfile() throws InterruptedException {
+ OfflinePlayer player = new OfflinePlayerMock("Offline Test Player 2");
+
+ Assertions.assertFalse(PlayerProfile.find(player).isPresent());
+
+ // This loads the profile asynchronously
+ Assertions.assertFalse(PlayerProfile.request(player));
+ }
+
+ @Test
+ public void testHashCode() throws InterruptedException {
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ Assertions.assertEquals(player.getUniqueId().hashCode(), profile.hashCode());
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/researches/TestProfileResearches.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/researches/TestProfileResearches.java
new file mode 100644
index 000000000..f584c0b49
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/researches/TestProfileResearches.java
@@ -0,0 +1,100 @@
+package io.github.thebusybiscuit.slimefun4.tests.researches;
+
+import java.util.Arrays;
+
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+
+public class TestProfileResearches {
+
+ private static ServerMock server;
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testSetResearched() throws InterruptedException {
+ SlimefunPlugin.getRegistry().setResearchingEnabled(true);
+
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> profile.setResearched(null, true));
+
+ NamespacedKey key = new NamespacedKey(plugin, "player_profile_test");
+ Research research = new Research(key, 250, "Test", 100);
+ research.register();
+
+ Assertions.assertFalse(profile.isDirty());
+ profile.setResearched(research, true);
+ Assertions.assertTrue(profile.isDirty());
+
+ Assertions.assertTrue(profile.hasUnlocked(research));
+ }
+
+ @Test
+ public void testHasUnlocked() throws InterruptedException {
+ SlimefunPlugin.getRegistry().setResearchingEnabled(true);
+
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ NamespacedKey key = new NamespacedKey(plugin, "player_profile_test");
+ Research research = new Research(key, 280, "Test", 100);
+ research.register();
+
+ profile.setResearched(research, true);
+
+ Assertions.assertTrue(profile.hasUnlocked(research));
+ Assertions.assertTrue(profile.hasUnlocked(null));
+
+ profile.setResearched(research, false);
+ Assertions.assertFalse(profile.hasUnlocked(research));
+
+ // Researches are disabled now, so this method should pass
+ // Whether Research#isEnabled() works correctly is covered elsewhere
+ SlimefunPlugin.getRegistry().setResearchingEnabled(false);
+ Assertions.assertTrue(profile.hasUnlocked(research));
+ }
+
+ @Test
+ public void testGetResearches() throws InterruptedException {
+ SlimefunPlugin.getRegistry().setResearchingEnabled(true);
+
+ Player player = server.addPlayer();
+ PlayerProfile profile = TestUtilities.awaitProfile(player);
+
+ Assertions.assertTrue(profile.getResearches().isEmpty());
+
+ NamespacedKey key = new NamespacedKey(plugin, "player_profile_test");
+ Research research = new Research(key, 260, "Test", 100);
+ research.register();
+
+ profile.setResearched(research, true);
+ Assertions.assertIterableEquals(Arrays.asList(research), profile.getResearches());
+
+ profile.setResearched(research, false);
+ Assertions.assertTrue(profile.getResearches().isEmpty());
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/researches/TestResearches.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/researches/TestResearches.java
new file mode 100644
index 000000000..e97ec0adb
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/researches/TestResearches.java
@@ -0,0 +1,172 @@
+package io.github.thebusybiscuit.slimefun4.tests.researches;
+
+import java.util.Optional;
+
+import org.bukkit.GameMode;
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.entity.Player;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.core.researching.Research;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TestResearches {
+
+ private static ServerMock server;
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testResearchGetters() {
+ NamespacedKey key = new NamespacedKey(plugin, "test");
+ Research research = new Research(key, 0, "Test", 100);
+
+ Assertions.assertEquals(key, research.getKey());
+ Assertions.assertEquals(100, research.getCost());
+
+ Assertions.assertFalse(Research.getResearch(null).isPresent());
+ Assertions.assertFalse(Research.getResearch(key).isPresent());
+ }
+
+ @Test
+ public void testResearchCost() {
+ NamespacedKey key = new NamespacedKey(plugin, "cost_test");
+ Research research = new Research(key, 5, "Cost Test", 100);
+
+ Assertions.assertEquals(100, research.getCost());
+
+ research.setCost(3000);
+ Assertions.assertEquals(3000, research.getCost());
+
+ // Negative values are not allowed
+ Assertions.assertThrows(IllegalArgumentException.class, () -> research.setCost(-100));
+ }
+
+ @Test
+ public void testResearchRegistration() {
+ NamespacedKey key = new NamespacedKey(plugin, "testResearch");
+ Research research = new Research(key, 1, "Test", 100);
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RESEARCH_TEST", new CustomItem(Material.TORCH, "&bResearch Test"));
+ research.addItems(item, null);
+ research.register();
+
+ SlimefunPlugin.getRegistry().setResearchingEnabled(true);
+
+ Assertions.assertTrue(research.isEnabled());
+ Assertions.assertEquals(research, item.getResearch());
+ Assertions.assertTrue(SlimefunPlugin.getRegistry().getResearches().contains(research));
+
+ Optional optional = Research.getResearch(key);
+ Assertions.assertTrue(optional.isPresent());
+ Assertions.assertEquals(research, optional.get());
+ }
+
+ @Test
+ public void testDisabledResearch() {
+ NamespacedKey key = new NamespacedKey(plugin, "disabledResearch");
+ Research research = new Research(key, 2, "Test", 100);
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RESEARCH_TEST", new CustomItem(Material.TORCH, "&bResearch Test"));
+ research.addItems(item);
+
+ SlimefunPlugin.getRegistry().setResearchingEnabled(true);
+ SlimefunPlugin.getResearchCfg().setValue(key.getNamespace() + '.' + key.getKey() + ".enabled", false);
+ research.register();
+
+ Assertions.assertFalse(research.isEnabled());
+ Assertions.assertNull(item.getResearch());
+ }
+
+ @Test
+ public void testResearchGloballyDisabled() {
+ NamespacedKey key = new NamespacedKey(plugin, "globallyDisabledResearch");
+ Research research = new Research(key, 3, "Test", 100);
+
+ SlimefunPlugin.getRegistry().setResearchingEnabled(true);
+ Assertions.assertTrue(research.isEnabled());
+ SlimefunPlugin.getRegistry().setResearchingEnabled(false);
+ Assertions.assertFalse(research.isEnabled());
+ SlimefunPlugin.getRegistry().setResearchingEnabled(true);
+ }
+
+ @Test
+ public void testAddItems() {
+ NamespacedKey key = new NamespacedKey(plugin, "addItemsResearch");
+ Research research = new Research(key, 17, "Test", 100);
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "RESEARCH_ITEMS_TEST", new CustomItem(Material.LAPIS_LAZULI, "&9Adding items is fun"));
+ item.register(plugin);
+
+ research.addItems(item.getItem(), null);
+
+ Assertions.assertTrue(research.getAffectedItems().contains(item));
+ Assertions.assertEquals(research, item.getResearch());
+ }
+
+ @Test
+ public void testPlayerCanUnlockDisabledResearch() {
+ SlimefunPlugin.getRegistry().setResearchingEnabled(false);
+
+ Player player = server.addPlayer();
+ NamespacedKey key = new NamespacedKey(plugin, "disabledUnlockableResearch");
+ Research research = new Research(key, 567, "Test", 100);
+
+ Assertions.assertTrue(research.canUnlock(player));
+
+ SlimefunPlugin.getRegistry().setResearchingEnabled(true);
+ }
+
+ @Test
+ public void testFreeCreativeResearch() {
+ SlimefunPlugin.getRegistry().setResearchingEnabled(true);
+
+ Player player = server.addPlayer();
+ NamespacedKey key = new NamespacedKey(plugin, "freeCreativeResearch");
+ Research research = new Research(key, 153, "Test", 100);
+
+ SlimefunPlugin.getRegistry().setFreeCreativeResearchingEnabled(false);
+
+ player.setGameMode(GameMode.SURVIVAL);
+ Assertions.assertFalse(research.canUnlock(player));
+
+ player.setGameMode(GameMode.CREATIVE);
+ Assertions.assertFalse(research.canUnlock(player));
+
+ SlimefunPlugin.getRegistry().setFreeCreativeResearchingEnabled(true);
+
+ player.setGameMode(GameMode.SURVIVAL);
+ Assertions.assertFalse(research.canUnlock(player));
+
+ player.setGameMode(GameMode.CREATIVE);
+ Assertions.assertTrue(research.canUnlock(player));
+ }
+
+ @Test
+ public void testUnlockableResearch() {
+ Player player = server.addPlayer();
+ NamespacedKey key = new NamespacedKey(plugin, "freeCreativeResearch");
+ Research research = new Research(key, 235, "Test", 4);
+
+ Assertions.assertFalse(research.canUnlock(player));
+ player.setLevel(8);
+ Assertions.assertTrue(research.canUnlock(player));
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestBlockDataService.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestBlockDataService.java
new file mode 100644
index 000000000..8d4bf2cbf
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestBlockDataService.java
@@ -0,0 +1,51 @@
+package io.github.thebusybiscuit.slimefun4.tests.services;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.slimefun4.core.services.BlockDataService;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+
+public class TestBlockDataService {
+
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testInitialization() {
+ BlockDataService service = new BlockDataService(plugin, "test");
+ Assertions.assertEquals(new NamespacedKey(plugin, "test"), service.getKey());
+ }
+
+ @Test
+ public void testTileEntities() {
+ BlockDataService service = new BlockDataService(plugin, "test");
+
+ Assertions.assertFalse(service.isTileEntity(null));
+ Assertions.assertFalse(service.isTileEntity(Material.AIR));
+ Assertions.assertFalse(service.isTileEntity(Material.DIRT));
+
+ Assertions.assertTrue(service.isTileEntity(Material.CHEST));
+ Assertions.assertTrue(service.isTileEntity(Material.ENDER_CHEST));
+ Assertions.assertTrue(service.isTileEntity(Material.ENCHANTING_TABLE));
+ Assertions.assertTrue(service.isTileEntity(Material.DISPENSER));
+ Assertions.assertTrue(service.isTileEntity(Material.FURNACE));
+ Assertions.assertTrue(service.isTileEntity(Material.PLAYER_HEAD));
+ Assertions.assertTrue(service.isTileEntity(Material.PLAYER_WALL_HEAD));
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestItemDataService.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestItemDataService.java
new file mode 100644
index 000000000..9360834de
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestItemDataService.java
@@ -0,0 +1,68 @@
+package io.github.thebusybiscuit.slimefun4.tests.services;
+
+import java.util.Optional;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.slimefun4.core.services.CustomItemDataService;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+
+public class TestItemDataService {
+
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testInitialization() {
+ CustomItemDataService service = new CustomItemDataService(plugin, "test");
+ Assertions.assertEquals(new NamespacedKey(plugin, "test"), service.getKey());
+ }
+
+ @Test
+ public void testSetDataItem() {
+ CustomItemDataService service = new CustomItemDataService(plugin, "test");
+ ItemStack item = new ItemStack(Material.EMERALD);
+
+ service.setItemData(item, "Hello World");
+ Optional data = service.getItemData(item);
+
+ Assertions.assertTrue(data.isPresent());
+ Assertions.assertEquals("Hello World", data.get());
+ }
+
+ @Test
+ public void testSetDataItemMeta() {
+ CustomItemDataService service = new CustomItemDataService(plugin, "test");
+ ItemStack item = new ItemStack(Material.EMERALD);
+ ItemMeta meta = item.getItemMeta();
+ service.setItemData(meta, "Hello World");
+
+ Optional data = service.getItemData(meta);
+ Assertions.assertTrue(data.isPresent());
+ Assertions.assertEquals("Hello World", data.get());
+
+ item.setItemMeta(meta);
+
+ Optional data2 = service.getItemData(item);
+ Assertions.assertTrue(data2.isPresent());
+ Assertions.assertEquals("Hello World", data2.get());
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestRecipeService.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestRecipeService.java
new file mode 100644
index 000000000..0ee1d0458
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestRecipeService.java
@@ -0,0 +1,139 @@
+package io.github.thebusybiscuit.slimefun4.tests.services;
+
+import java.util.Optional;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.FurnaceRecipe;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.Recipe;
+import org.bukkit.inventory.RecipeChoice;
+import org.bukkit.inventory.RecipeChoice.MaterialChoice;
+import org.bukkit.inventory.ShapedRecipe;
+import org.bukkit.inventory.ShapelessRecipe;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import be.seeseemelk.mockbukkit.ServerMock;
+import io.github.thebusybiscuit.slimefun4.core.services.MinecraftRecipeService;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+
+public class TestRecipeService {
+
+ private static ServerMock server;
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ server = MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testRecipe() {
+ MinecraftRecipeService service = new MinecraftRecipeService(plugin);
+
+ NamespacedKey key = new NamespacedKey(plugin, "furnace_recipe_test");
+ ItemStack result = new ItemStack(Material.EMERALD_BLOCK);
+ FurnaceRecipe recipe = new FurnaceRecipe(key, result, new MaterialChoice(Material.DIAMOND), 1, 2);
+ server.addRecipe(recipe);
+
+ service.refresh();
+
+ Recipe[] recipes = service.getRecipesFor(result);
+ Assertions.assertEquals(1, recipes.length);
+ Assertions.assertEquals(recipe, recipes[0]);
+ }
+
+ @Test
+ public void testNoRecipes() {
+ MinecraftRecipeService service = new MinecraftRecipeService(plugin);
+ service.refresh();
+
+ Assertions.assertEquals(0, service.getRecipesFor(null).length);
+ Assertions.assertEquals(0, service.getRecipesFor(new ItemStack(Material.BEDROCK)).length);
+ }
+
+ @Test
+ public void testFurnaceOutput() {
+ MinecraftRecipeService service = new MinecraftRecipeService(plugin);
+
+ NamespacedKey key = new NamespacedKey(plugin, "furnace_recipe_test2");
+ ItemStack result = new ItemStack(Material.GOLD_BLOCK);
+ MaterialChoice materials = new MaterialChoice(Material.DIRT, Material.COBBLESTONE);
+ FurnaceRecipe recipe = new FurnaceRecipe(key, result, materials, 1, 2);
+ server.addRecipe(recipe);
+
+ service.refresh();
+
+ Assertions.assertFalse(service.getFurnaceOutput(null).isPresent());
+ Assertions.assertFalse(service.getFurnaceOutput(new ItemStack(Material.BEDROCK)).isPresent());
+
+ Optional optional = service.getFurnaceOutput(new ItemStack(Material.DIRT));
+ Assertions.assertTrue(optional.isPresent());
+ Assertions.assertEquals(result, optional.get());
+
+ Optional optional2 = service.getFurnaceOutput(new ItemStack(Material.COBBLESTONE));
+ Assertions.assertTrue(optional2.isPresent());
+ Assertions.assertEquals(result, optional2.get());
+ }
+
+ @Test
+ public void testBigShapedRecipe() {
+ MinecraftRecipeService service = new MinecraftRecipeService(plugin);
+
+ NamespacedKey key = new NamespacedKey(plugin, "shaped_recipe_9");
+ ShapedRecipe recipe = new ShapedRecipe(key, new ItemStack(Material.ENCHANTED_GOLDEN_APPLE));
+ MaterialChoice choice = new MaterialChoice(Material.TNT, Material.TNT_MINECART);
+
+ recipe.shape("t t", " t ", "t t");
+ recipe.setIngredient('t', choice);
+ server.addRecipe(recipe);
+ service.refresh();
+
+ RecipeChoice[] shape = service.getRecipeShape(recipe);
+ Assertions.assertArrayEquals(new RecipeChoice[] { choice, null, choice, null, choice, null, choice, null, choice }, shape);
+ }
+
+ @Test
+ public void testSmallShapedRecipe() {
+ MinecraftRecipeService service = new MinecraftRecipeService(plugin);
+
+ NamespacedKey key = new NamespacedKey(plugin, "shaped_recipe_4");
+ ShapedRecipe recipe = new ShapedRecipe(key, new ItemStack(Material.ENCHANTED_GOLDEN_APPLE));
+ MaterialChoice choice = new MaterialChoice(Material.TNT, Material.TNT_MINECART);
+
+ recipe.shape("tt", "tt");
+ recipe.setIngredient('t', choice);
+ server.addRecipe(recipe);
+ service.refresh();
+
+ RecipeChoice[] shape = service.getRecipeShape(recipe);
+ Assertions.assertArrayEquals(new RecipeChoice[] { choice, choice, null, choice, choice, null }, shape);
+ }
+
+ @Test
+ public void testShapelessRecipeShape() {
+ MinecraftRecipeService service = new MinecraftRecipeService(plugin);
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> service.getRecipeShape(null));
+
+ NamespacedKey key = new NamespacedKey(plugin, "shapeless_test");
+ ShapelessRecipe recipe = new ShapelessRecipe(key, new ItemStack(Material.TNT_MINECART));
+ MaterialChoice choice = new MaterialChoice(Material.TNT);
+ recipe.addIngredient(choice);
+
+ server.addRecipe(recipe);
+ service.refresh();
+
+ Assertions.assertArrayEquals(new RecipeChoice[] { choice }, service.getRecipeShape(recipe));
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestUpdaterService.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestUpdaterService.java
new file mode 100644
index 000000000..e30b33441
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TestUpdaterService.java
@@ -0,0 +1,59 @@
+package io.github.thebusybiscuit.slimefun4.tests.services;
+
+import java.io.File;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.slimefun4.api.SlimefunBranch;
+import io.github.thebusybiscuit.slimefun4.core.services.UpdaterService;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+
+public class TestUpdaterService {
+
+ private static SlimefunPlugin plugin;
+
+ private final File file = new File("test.jar");
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testDevelopmentBuilds() {
+ UpdaterService service = new UpdaterService(plugin, "DEV - 1 (git 123456)", file);
+ Assertions.assertEquals(SlimefunBranch.DEVELOPMENT, service.getBranch());
+ Assertions.assertTrue(service.getBranch().isOfficial());
+ }
+
+ @Test
+ public void testStableBuilds() {
+ UpdaterService service = new UpdaterService(plugin, "RC - 1 (git 123456)", file);
+ Assertions.assertEquals(SlimefunBranch.STABLE, service.getBranch());
+ Assertions.assertTrue(service.getBranch().isOfficial());
+ }
+
+ @Test
+ public void testUnofficialBuilds() {
+ UpdaterService service = new UpdaterService(plugin, "4.20 UNOFFICIAL", file);
+ Assertions.assertEquals(SlimefunBranch.UNOFFICIAL, service.getBranch());
+ Assertions.assertFalse(service.getBranch().isOfficial());
+ }
+
+ @Test
+ public void testUnknownBuilds() {
+ UpdaterService service = new UpdaterService(plugin, "I am special", file);
+ Assertions.assertEquals(SlimefunBranch.UNKNOWN, service.getBranch());
+ Assertions.assertFalse(service.getBranch().isOfficial());
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TextCustomTextureService.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TextCustomTextureService.java
new file mode 100644
index 000000000..07130c358
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/services/TextCustomTextureService.java
@@ -0,0 +1,75 @@
+package io.github.thebusybiscuit.slimefun4.tests.services;
+
+import java.util.Arrays;
+
+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.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.cscorelib2.config.Config;
+import io.github.thebusybiscuit.slimefun4.core.services.CustomTextureService;
+import io.github.thebusybiscuit.slimefun4.mocks.TestUtilities;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TextCustomTextureService {
+
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testInitialization() {
+ Config config = new Config("plugins/temporary");
+ CustomTextureService service = new CustomTextureService(config);
+ Assertions.assertFalse(service.isActive());
+ Assertions.assertNull(service.getVersion());
+
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "TEXTURE_TEST", new ItemStack(Material.LANTERN));
+ service.register(Arrays.asList(null, item, null), false);
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> service.register(null, false));
+
+ // These values should not have changed yet
+ Assertions.assertFalse(service.isActive());
+ Assertions.assertNull(service.getVersion());
+
+ Assertions.assertThrows(IllegalArgumentException.class, () -> service.getModelData(null));
+ }
+
+ @Test
+ public void testSetTexture() {
+ Config config = new Config("plugins/temporary");
+ CustomTextureService service = new CustomTextureService(config);
+ SlimefunItem item = TestUtilities.mockSlimefunItem(plugin, "TEXTURE_TEST", new ItemStack(Material.LANTERN));
+ String version = "Unit Test v1.0";
+
+ config.setValue(item.getID(), 300);
+ config.setValue("version", version);
+
+ service.register(Arrays.asList(item), false);
+
+ Assertions.assertTrue(service.isActive());
+ Assertions.assertEquals(version, service.getVersion());
+ Assertions.assertEquals(300, service.getModelData(item.getID()));
+
+ ItemStack stack = new ItemStack(Material.DIAMOND);
+ service.setTexture(stack, item.getID());
+
+ Assertions.assertTrue(stack.getItemMeta().hasCustomModelData());
+ Assertions.assertEquals(300, stack.getItemMeta().getCustomModelData());
+ }
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/utils/TestItemStackWrapper.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/utils/TestItemStackWrapper.java
new file mode 100644
index 000000000..9aaae920e
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/utils/TestItemStackWrapper.java
@@ -0,0 +1,50 @@
+package io.github.thebusybiscuit.slimefun4.tests.utils;
+
+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.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
+import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+
+public class TestItemStackWrapper {
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testEquality() {
+ ItemStack item = new CustomItem(Material.LAVA_BUCKET, "&4SuperHot.exe", "", "&6Hello");
+ ItemStackWrapper wrapper = new ItemStackWrapper(item);
+
+ Assertions.assertEquals(item.getType(), wrapper.getType());
+ Assertions.assertEquals(item.getItemMeta(), wrapper.getItemMeta());
+ Assertions.assertTrue(SlimefunUtils.isItemSimilar(wrapper, item, true));
+ }
+
+ @Test
+ public void testImmutability() {
+ ItemStack item = new CustomItem(Material.LAVA_BUCKET, "&4SuperHot.exe", "", "&6Hello");
+ ItemStackWrapper wrapper = new ItemStackWrapper(item);
+
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> wrapper.setType(Material.BEDROCK));
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> wrapper.setAmount(3));
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> wrapper.setItemMeta(item.getItemMeta()));
+ Assertions.assertThrows(UnsupportedOperationException.class, () -> wrapper.addUnsafeEnchantment(null, 1));
+ }
+
+}
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/tests/utils/TestSoulboundItem.java b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/utils/TestSoulboundItem.java
new file mode 100644
index 000000000..ef0253991
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/tests/utils/TestSoulboundItem.java
@@ -0,0 +1,91 @@
+package io.github.thebusybiscuit.slimefun4.tests.utils;
+
+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.Test;
+
+import be.seeseemelk.mockbukkit.MockBukkit;
+import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
+import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound;
+import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
+import me.mrCookieSlime.Slimefun.SlimefunPlugin;
+import me.mrCookieSlime.Slimefun.Objects.Category;
+import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
+
+public class TestSoulboundItem {
+
+ private static SlimefunPlugin plugin;
+
+ @BeforeAll
+ public static void load() {
+ MockBukkit.mock();
+ plugin = MockBukkit.load(SlimefunPlugin.class);
+ }
+
+ @AfterAll
+ public static void unload() {
+ MockBukkit.unmock();
+ }
+
+ @Test
+ public void testNullAndAir() {
+ Assertions.assertThrows(IllegalArgumentException.class, () -> SlimefunUtils.setSoulbound(null, true));
+
+ ItemStack item = new ItemStack(Material.AIR);
+ Assertions.assertThrows(IllegalArgumentException.class, () -> SlimefunUtils.setSoulbound(item, true));
+
+ Assertions.assertFalse(SlimefunUtils.isSoulbound(null));
+ Assertions.assertFalse(SlimefunUtils.isSoulbound(item));
+ }
+
+ @Test
+ public void testSetSoulbound() {
+ ItemStack item = new CustomItem(Material.DIAMOND, "&cI wanna be soulbound!");
+
+ Assertions.assertFalse(SlimefunUtils.isSoulbound(item));
+
+ SlimefunUtils.setSoulbound(item, true);
+ Assertions.assertTrue(SlimefunUtils.isSoulbound(item));
+ Assertions.assertEquals(1, item.getItemMeta().getLore().size());
+
+ SlimefunUtils.setSoulbound(item, false);
+ Assertions.assertFalse(SlimefunUtils.isSoulbound(item));
+ Assertions.assertEquals(0, item.getItemMeta().getLore().size());
+ }
+
+ @Test
+ public void testDoubleCalls() {
+ ItemStack item = new CustomItem(Material.DIAMOND, "&cI wanna be soulbound!");
+
+ SlimefunUtils.setSoulbound(item, true);
+ SlimefunUtils.setSoulbound(item, true);
+ Assertions.assertTrue(SlimefunUtils.isSoulbound(item));
+ Assertions.assertEquals(1, item.getItemMeta().getLore().size());
+
+ SlimefunUtils.setSoulbound(item, false);
+ SlimefunUtils.setSoulbound(item, false);
+ Assertions.assertFalse(SlimefunUtils.isSoulbound(item));
+ Assertions.assertEquals(0, item.getItemMeta().getLore().size());
+ }
+
+ @Test
+ public void testSoulboundSlimefunItem() {
+ SlimefunItem item = new SoulboundMock(new Category(new NamespacedKey(plugin, "soulbound_category"), new CustomItem(Material.REDSTONE, "&4Walshrus forever")));
+ item.register(plugin);
+
+ Assertions.assertTrue(SlimefunUtils.isSoulbound(item.getItem()));
+ }
+
+ private class SoulboundMock extends SlimefunItem implements Soulbound {
+
+ public SoulboundMock(Category category) {
+ super(category, new CustomItem(Material.REDSTONE, "&4Almighty Redstone"), "MOCK_SOULBOUND", null, new ItemStack[9]);
+ }
+
+ }
+
+}