diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6b8e625b7..d9222d1ed 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,5 @@ ## Description - + ## Changes @@ -9,8 +9,10 @@ ## Checklist - + + - [ ] I have fully tested the proposed changes and promise that they will not break everything into chaos. - [ ] I have also tested the proposed changes in combination with various popular addons and can confirm my changes do not break them. - [ ] I followed the existing code standards and didn't mess up the formatting. -- [ ] I did my best to add documentation to any public classes or methods I added. \ No newline at end of file +- [ ] I did my best to add documentation to any public classes or methods I added. +- [ ] I added sufficient Unit Tests to cover my code. diff --git a/.gitignore b/.gitignore index 38fc3e2e3..4a193c6a0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,13 @@ /bin/ +/target/ +/plugins/ +/sonar/ +/.settings/ +/.idea/ + +dependency-reduced-pom.xml + .classpath .project -/.settings/ *.iml -/target -/.idea/ -dependency-reduced-pom.xml -javadoc.xml .DS_Store \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4839e5d14..6b5fdbb24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,9 +21,13 @@ #### Additions * Added Ukrainian translations +* Added /sf backpack to restore lost backpacks +* Added automated Unit Tests #### Changes +* Little performance improvements * Bandages, Rags and Splints will no longer be consumed if your health is full and you are not on fire +* Player Profiles (researches and stuff) are now loaded completely asynchronously #### Fixes * Fixed #1824 @@ -32,6 +36,10 @@ * Fixed #1843 * Fixed #1873 * Fixed Electric Smeltery not prioritisting recipes +* Fixed #1851 +* Fixed #1891 +* Fixed #1893 +* Fixed #1897 ## Release Candidate 11 (25 Apr 2020) diff --git a/README.md b/README.md index 63565f75f..a68709f08 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It currently adds over **500 new items and recipes** to Minecraft ([Read more ab But it also comes with a lot of Addons too!
Check out our [Addons](https://github.com/TheBusyBiscuit/Slimefun4/wiki/Addons), you may find exactly what you were looking for. -## Quick navigation +### Quick navigation * **[Download Slimefun4](#download-slimefun-4)** * **[Discord Support Server](#discord)** * **[Bug Tracker](https://github.com/TheBusyBiscuit/Slimefun4/issues)** @@ -21,11 +21,11 @@ Check out our [Addons](https://github.com/TheBusyBiscuit/Slimefun4/wiki/Addons), (See also: [How to install Slimefun](https://github.com/TheBusyBiscuit/Slimefun4/wiki/Installing-Slimefun))
Slimefun 4 can be downloaded **for free** on our builds page.
We currently provide two versions of Slimefun, development builds and "stable" builds.
-Here is a full summary of the differences between these two versions of Slimefun. +Here is a full summary of the differences between different versions of Slimefun. | | development (latest) | "stable" | | ------------------ | -------- | -------- | -| **Minecraft version(s)** | 1.13.X - 1.15.X | 1.13.X - 1.15.X | +| **Minecraft version(s)** | :video_game: 1.13.X - 1.15.X | :video_game: 1.13.X - 1.15.X | | **testing before release** | :x: | :heavy_check_mark: | | **latest content** | :heavy_check_mark: | :x: | | **Discord support** | :heavy_check_mark: | :x: | @@ -36,9 +36,9 @@ Here is a full summary of the differences between these two versions of Slimefun **! We wholeheartedly recommend you to use development builds !** -"Stable" builds do ___not___ receive frequent updates or fast patches. Therefore they will always contain many more bugs than the develoment builds. We will also ___not___ accept or review any bug reports from "stable" builds since they are basically just outdated development builds.
-**Why use a "stable" build then?** While "stable" builds contain more bugs than development builds you can be sure that they will not include any game-breaking issues, development builds almost never contain such issues either. But if your server/business heavily depends on a version of Slimefun that you need to rely on, you are forgiven if you choose the "stable" branch.
-**What exactly are these "stable" builds then and why do you put them in quotes?** "Stable" builds are literally just outdated development builds that seemed to run fine without any major issues. But they are not exactly bug-free hence why calling them stable would be hipocritical. However these builds can only really stay "stable" if enough people use the development builds to test them and report bugs. Otherwise potential issues may go unnoticed and slip into "stable" builds. Again, we really recommend you to choose the development builds. But since many people really wanted "stable" builds, they are now an option too. +"Stable" builds do not receive frequent updates or fast patches. Therefore they will always contain a lot more bugs than the develoment builds. We will also not accept or review any bug reports from "stable" builds. These are just old development builds that seemed to run without any major issues.
+**Why use a "stable" build then?** While "stable" builds contain more bugs than development builds you can be sure that they will not include game-breaking issues, development builds almost never contain such issues either. But if your server/business heavily depends on a version of Slimefun that you need to rely on, you are forgiven if you choose the "stable" branch.
+**What exactly are these "stable" builds then and why do you put them in quotes?** "Stable" builds are literally just outdated development builds that seemed to run fine without any major issues. But they are not exactly bug-free hence why actually calling them stable would be hypocritical. However these builds can only really stay "stable" if there are enough people using development builds to report bugs. Otherwise potential issues may go unnoticed and slip into "stable" builds. Again, we really recommend you to choose the development builds. But since many people really wanted "stable" builds, they are now an option too. Whatever version of Slimefun you choose, we also recommend you to keep `auto-updates` enabled to receive automatic patches and fixes for Slimefun! @@ -69,7 +69,7 @@ Slimefun has a (detailed and well-maintained - *cough*) Wiki for new players, ma expanding the wiki to help grow our community and help out new users of this plugin. https://github.com/TheBusyBiscuit/Slimefun4/wiki -### Highlighted Articles +##### Highlighted Articles * [What is Slimefun?](https://github.com/TheBusyBiscuit/Slimefun4/wiki/Slimefun-in-a-nutshell) * [How to install Slimefun](https://github.com/TheBusyBiscuit/Slimefun4/wiki/Installing-Slimefun) * [Addons for Slimefun 4](https://github.com/TheBusyBiscuit/Slimefun4/wiki/Addons) @@ -88,33 +88,47 @@ Slimefun 4 is an Open-Source project and licensed under Over 100 people have already contributed to this amazing project. You guys are awesome.
Please consider helping us maintain this project too, your engagement keeps the project alive <3. -

- - GitHub contributors - -

- -### Pull requests -This is an open-source community project, so **your contributions keep this plugin alive!**
-Pull Requests can be fixes, changes or even additions, but please keep in mind that if you add too much content to Slimefun 4, you should maybe consider making an Addon for it instead ([Developer Guide](https://github.com/TheBusyBiscuit/Slimefun4/wiki/Developer-Guide)). - ### Translations Slimefun4 has recently added suport for translations, note that translations are still _work in progress_.
So not everything may be available for translation yet.
[Read more...](https://github.com/TheBusyBiscuit/Slimefun4/wiki/Translating-Slimefun) +### Pull requests +This is an open-source community project, so **your contributions keep this plugin alive!**
+Pull Requests can be fixes, changes or even additions, but please keep in mind that if you add too much content to Slimefun 4, you should maybe consider making an Addon for it instead ([Developer Guide](https://github.com/TheBusyBiscuit/Slimefun4/wiki/Developer-Guide)). + +### Code Quality +Slimefun uses [Sonarcloud.io](https://sonarcloud.io/dashboard?id=TheBusyBiscuit_Slimefun4) to monitor Code Quality. + +| [Overall Maintainability](https://sonarcloud.io/documentation/user-guide/metric-definitions/#maintainability) | "Code Smells" | "Technical Debt" | Test Coverage | +| ---- | ---- | ---- | ---- | +| [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=TheBusyBiscuit_Slimefun4&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=TheBusyBiscuit_Slimefun4) | [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=TheBusyBiscuit_Slimefun4&metric=code_smells)](https://sonarcloud.io/dashboard?id=TheBusyBiscuit_Slimefun4) | [![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=TheBusyBiscuit_Slimefun4&metric=sqale_index)](https://sonarcloud.io/dashboard?id=TheBusyBiscuit_Slimefun4) | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=TheBusyBiscuit_Slimefun4&metric=coverage)](https://sonarcloud.io/dashboard?id=TheBusyBiscuit_Slimefun4) | + +##### "Code Smells" +Code Smells are portions of the source code that are confusing, lack documentation or are just done very badly in general. These code smells should be held to a bare minimum. + +_Please contact us on [Discord](#discord) before working on any code smells. Some design patterns may not be changed abruptly because an addon might depend on them._ + +##### "Technical Debt" +Technical Debt is basically an estimate for how long it would take to fix all issues and code smells. + +##### Test Coverage +Slimefun now also uses Automated Tests to determine whether an update could break something. The coverage shows how much these tests cover. Higher coverage means less breaking changes and in turn better and more reliable builds. +Due to this being a very huge project though, getting to `100% coverage` is probably impossible. But increasing that number even slightly still helps. So feel free to write Unit Tests for Slimefun and place them in the [/src/test/java/](https://github.com/TheBusyBiscuit/Slimefun4/tree/master/src/test/java) folder. + + ## Disclaimers -Slimefun4 uses various systems that collect usage information or download automatic updates as well as the latest information about the project.
-We do __not__ collect any personal information from you but here is a full list of what services may collect or download other kinds of data. +Slimefun4 uses various systems that collect usage information or download automatic updates as well as the latest information about the project. +We do not collect any personal information from you but there are some services that may gather or download some data. You can opt-out of the Auto-Updater and stats collection at any time. -### Auto-Updates +#### Auto-Updates Slimefun4 uses an Auto-Updater which connects to https://thebusybiscuit.github.io/builds/ to check for and download updates.
-This behaviour is enabled by default but can be turned off under `/plugins/Slimefun/config.yml`
+This behaviour is enabled by default but can be turned off under `/plugins/Slimefun/config.yml`.
We highly recommend you to keep this on at any time though, as you could be missing out on important patches. -### Metrics and Statistics +#### Metrics and Statistics Slimefun4 uses [bStats](https://bstats.org/plugin/bukkit/Slimefun/4574) to collect anonymous information about the usage of this plugin.
This is solely for statistical purposes, as we are interested in how Servers/Players use this plugin.
All available data is anonymous and aggregated, at no point can we see individual server or player information.
@@ -123,12 +137,12 @@ All of the collected data is publicly accessible: https://bstats.org/plugin/bukk You can also disable this behaviour under `/plugins/bStats/config.yml`.
For more info see [bStats' Privacy Policy](https://bstats.org/privacy-policy) -### GitHub Integration +#### GitHub Integration Lastly, Slimefun4 connects to https://api.github.com/ to gather information about this open-source project.
No information about your Minecraft Server is sent to GitHub. This information includes (but is not limited to) -* list of contributors, their username and profile link (from the repositories `TheBusyBiscuit/Slimefun4`, `TheBusyBiscuit/Slimefun4-Wiki` and `TheBusyBiscuit/Slimefun4-Resourcepack`) +* list of contributors, their username and profile link (from the repositories `TheBusyBiscuit/Slimefun4`, `Slimefun/Slimefun-Wiki` and `Slimefun/Resourcepack`) * amount of open issues in this repository * amount of pending pull requests in this repository * amount of stars in this repository diff --git a/pom.xml b/pom.xml index 7792372c2..2fb3e04f8 100644 --- a/pom.xml +++ b/pom.xml @@ -1,29 +1,51 @@ - - + + 4.0.0 - me.mrCookieSlime + io.github.thebusybiscuit Slimefun - - + + 4.3-UNOFFICIAL - + 2013 + jar + + Slimefun is a Bukkit / Spigot plugin that simulates a modpack-like atmosphere by adding over 500 new items and recipes to your Minecraft Server. + https://github.com/TheBusyBiscuit/Slimefun4 + UTF-8 1.8 1.8 - - + + 1.15.2 https://hub.spigotmc.org/javadocs/bukkit/ - + - TheBusyBiscuit_Slimefun4 - thebusybiscuit-github - https://sonarcloud.io + TheBusyBiscuit_Slimefun4 + thebusybiscuit-github + https://sonarcloud.io + DEBUG + target/site/jacoco/jacoco.xml - + + + GitHub Issues + https://github.com/TheBusyBiscuit/Slimefun4/issues + + + + + GNU General Public License v3.0 + https://github.com/TheBusyBiscuit/Slimefun4/blob/master/LICENSE + repo + + + paper-repo @@ -50,25 +72,29 @@ http://repo.extendedclip.com/content/repositories/placeholderapi/ - + ${project.basedir}/src/main/java + ${project.basedir}/src/test/java + ${project.name} v${project.version} - + + org.apache.maven.plugins maven-compiler-plugin 3.8.1 - - + + **/package-info.java - + + org.apache.maven.plugins maven-source-plugin @@ -82,15 +108,54 @@ - + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M4 + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + 3.7.0.1746 + + + + + org.jacoco + jacoco-maven-plugin + 0.8.5 + + + + prepare + + prepare-agent + + + + + report + test + + report + + + + + + org.apache.maven.plugins maven-shade-plugin 3.2.3 - + - - + + org.bstats @@ -101,18 +166,18 @@ me.mrCookieSlime.Slimefun.cscorelib2 - + - - *:* - - META-INF/* - - + + *:* + + META-INF/* + + - + package @@ -122,83 +187,84 @@ - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.2.0 - - - ${project.basedir} - docs - - Slimefun4 - Javadocs - Slimefun4 - Javadocs - false - -html5 - - - - ${bukkit.javadocs} - - - - - - Slimefun4 - API - io.github.thebusybiscuit.slimefun4.api* - - - Slimefun4 - Core packages - io.github.thebusybiscuit.slimefun4.core* - - - Slimefun4 - Implementations - io.github.thebusybiscuit.slimefun4.implementation* - - - Slimefun4 - Common utility packages - io.github.thebusybiscuit.slimefun4.utils* - - - Slimefun4 - Item Implementations - io.github.thebusybiscuit.slimefun4.implementation.items* - - - Slimefun4 - Old packages - me.mrCookieSlime.Slimefun* - - - - + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + + ${project.basedir} + docs + + Slimefun4 - Javadocs + Slimefun4 - Javadocs + false + -html5 + + + + ${bukkit.javadocs} + + + + + + Slimefun4 - API + io.github.thebusybiscuit.slimefun4.api* + + + Slimefun4 - Core packages + io.github.thebusybiscuit.slimefun4.core* + + + Slimefun4 - Implementations + io.github.thebusybiscuit.slimefun4.implementation* + + + Slimefun4 - Common utility packages + io.github.thebusybiscuit.slimefun4.utils* + + + Slimefun4 - Item Implementations + io.github.thebusybiscuit.slimefun4.implementation.items* + + + Slimefun4 - Old packages + me.mrCookieSlime.Slimefun* + + + + - + - - + + ${basedir}/src/main/resources true - + * languages/* - - + + ${basedir} - + LICENSE - + - + org.bukkit bukkit @@ -211,7 +277,27 @@ 8081bb4fe4 provided - + + + + org.junit.jupiter + junit-jupiter + 5.5.1 + test + + + com.github.seeseemelk + MockBukkit + v1.15-d69d9ca9cb-1 + test + + + org.mockito + mockito-core + 3.3.3 + test + + com.github.thebusybiscuit @@ -225,7 +311,7 @@ 1.7 compile - + com.sk89q.worldedit @@ -245,7 +331,7 @@ 3cd370b5d8 provided - + me.minebuilders diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java index 915dd519c..646dd16f7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/MinecraftVersion.java @@ -11,7 +11,7 @@ import me.mrCookieSlime.Slimefun.SlimefunPlugin; * */ public enum MinecraftVersion { - + /** * This constant represents Minecraft (Java Edition) Version 1.14 * (The Update Aquatic) @@ -34,7 +34,13 @@ public enum MinecraftVersion { * This constant represents an exceptional state in which we were unable * to identify the Minecraft Version we are using */ - UNKNOWN("Unknown"); + UNKNOWN("Unknown"), + + /** + * This is a very special state that represents the environment being a Unit + * Test and not an actual running Minecraft Server. + */ + UNIT_TEST("Unit Test Environment"); private final String name; private final String prefix; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/MultiBlockInteractEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/MultiBlockInteractEvent.java index 8068ee924..ce7d0c02f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/MultiBlockInteractEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/MultiBlockInteractEvent.java @@ -18,9 +18,9 @@ public class MultiBlockInteractEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); - private Player player; - private MultiBlock multiBlock; - private Block clickedBlock; + private final Player player; + private final MultiBlock multiBlock; + private final Block clickedBlock; private boolean cancelled; public HandlerList getHandlers() { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/ResearchUnlockEvent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/ResearchUnlockEvent.java index 2fcbfcc94..8f0c391ae 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/ResearchUnlockEvent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/events/ResearchUnlockEvent.java @@ -5,7 +5,7 @@ import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import me.mrCookieSlime.Slimefun.Objects.Research; +import io.github.thebusybiscuit.slimefun4.core.researching.Research; /** * This {@link Event} is called whenever a {@link Player} unlocks a {@link Research}. diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/exceptions/PrematureCodeException.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/exceptions/PrematureCodeException.java index f05218a56..d02c773aa 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/exceptions/PrematureCodeException.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/exceptions/PrematureCodeException.java @@ -5,7 +5,7 @@ import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; /** * A {@link PrematureCodeException} is thrown when a {@link SlimefunAddon} tried * to access Slimefun code before Slimefun was enabled. - * Always let your code inside onEnable() or later, never on class initialization. + * Always let your code run inside onEnable() or later, never on class initialization. * * @author TheBusyBiscuit * diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java index 9fc120dcc..fb35b2b44 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/geo/ResourceManager.java @@ -40,7 +40,7 @@ import me.mrCookieSlime.Slimefun.api.BlockStorage; public class ResourceManager { private final int[] backgroundSlots = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 17, 18, 26, 27, 35, 36, 44, 45, 46, 48, 49, 50, 52, 53 }; - private final ItemStack chunkTexture = SkullItem.fromBase64("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODQ0OWI5MzE4ZTMzMTU4ZTY0YTQ2YWIwZGUxMjFjM2Q0MDAwMGUzMzMyYzE1NzQ5MzJiM2M4NDlkOGZhMGRjMiJ9fX0="); + private final ItemStack chunkTexture = SkullItem.fromHash("8449b9318e33158e64a46ab0de121c3d40000e3332c1574932b3c849d8fa0dc2"); private final Config config; public ResourceManager(SlimefunPlugin plugin) { @@ -48,7 +48,8 @@ public class ResourceManager { } void register(GEOResource resource) { - boolean enabled = config.getOrSetDefault(resource.getKey().toString().replace(':', '.') + ".enabled", true); + String key = resource.getKey().getNamespace() + '.' + resource.getKey().getKey(); + boolean enabled = config.getOrSetDefault(key + ".enabled", true); if (enabled) { SlimefunPlugin.getRegistry().getGEOResources().add(resource); @@ -81,11 +82,11 @@ public class ResourceManager { if (value > 0) { int bound = resource.getMaxDeviation(); - + if (bound <= 0) { throw new IllegalStateException("GEO Resource \"" + resource.getKey() + "\" was misconfigured! getMaxDeviation() must return a value higher than zero!"); } - + value += ThreadLocalRandom.current().nextInt(bound); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java index 3292df9e3..c291e14a6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java @@ -10,8 +10,8 @@ import org.bukkit.Location; import org.bukkit.Particle; import org.bukkit.Particle.DustOptions; +import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager; import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener; -import me.mrCookieSlime.Slimefun.SlimefunPlugin; import me.mrCookieSlime.Slimefun.api.Slimefun; /** @@ -62,33 +62,53 @@ public abstract class Network { protected Location regulator; private Queue nodeQueue = new ArrayDeque<>(); - protected Set connectedLocations = new HashSet<>(); - protected Set regulatorNodes = new HashSet<>(); - protected Set connectorNodes = new HashSet<>(); - protected Set terminusNodes = new HashSet<>(); + private final NetworkManager manager; + protected final Set connectedLocations = new HashSet<>(); + protected final Set regulatorNodes = new HashSet<>(); + protected final Set connectorNodes = new HashSet<>(); + protected final Set terminusNodes = new HashSet<>(); - protected Network(Location regulator) { + protected Network(NetworkManager manager, Location regulator) { + this.manager = manager; this.regulator = regulator; + connectedLocations.add(regulator); nodeQueue.add(regulator.clone()); } + /** + * This returns the size of this {@link Network}. It is equivalent to the amount + * of {@link Location Locations} connected to this {@link Network}. + * + * @return The size of this {@link Network} + */ + public int getSize() { + return regulatorNodes.size() + connectorNodes.size() + terminusNodes.size(); + } + protected void addLocationToNetwork(Location l) { if (connectedLocations.contains(l)) { return; } connectedLocations.add(l.clone()); - handleLocationUpdate(l); + markDirty(l); } - public void handleLocationUpdate(Location l) { + /** + * This method marks the given {@link Location} as dirty and adds it to a {@link Queue} + * to handle this update. + * + * @param l + * The {@link Location} to update + */ + public void markDirty(Location l) { if (regulator.equals(l)) { - SlimefunPlugin.getNetworkManager().unregisterNetwork(this); - return; + manager.unregisterNetwork(this); + } + else { + nodeQueue.add(l.clone()); } - - nodeQueue.add(l.clone()); } /** @@ -117,7 +137,7 @@ public abstract class Network { } private void discoverStep() { - int maxSteps = SlimefunPlugin.getNetworkManager().getMaxSize(); + int maxSteps = manager.getMaxSize(); int steps = 0; while (nodeQueue.peek() != null) { @@ -128,7 +148,7 @@ public abstract class Network { if (classification != currentAssignment) { if (currentAssignment == NetworkComponent.REGULATOR || currentAssignment == NetworkComponent.CONNECTOR) { // Requires a complete rebuild of the network, so we just throw the current one away. - SlimefunPlugin.getNetworkManager().unregisterNetwork(this); + manager.unregisterNetwork(this); return; } else if (currentAssignment == NetworkComponent.TERMINUS) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/NetworkComponent.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/NetworkComponent.java index a567123c6..a741ceadd 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/NetworkComponent.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/NetworkComponent.java @@ -1,5 +1,7 @@ package io.github.thebusybiscuit.slimefun4.api.network; +import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager; + /** * This enum holds the different types of components a {@link Network} can have. * It is used for classification of nodes inside the {@link Network}. diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java index 84f008e25..c7a5f5cf2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerBackpack.java @@ -8,8 +8,9 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import io.github.thebusybiscuit.cscorelib2.config.Config; -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.BackpackListener; +import me.mrCookieSlime.Slimefun.api.Slimefun; /** * This class represents the instance of a {@link SlimefunBackpack} that is ready to @@ -59,25 +60,53 @@ public class PlayerBackpack { * The size of this Backpack */ public PlayerBackpack(PlayerProfile profile, int id, int size) { + if (size < 9 || size > 54 || size % 9 != 0) { + throw new IllegalArgumentException("Invalid size! Size must be one of: [9, 18, 27, 36, 45, 54]"); + } + this.profile = profile; this.id = id; this.cfg = profile.getConfig(); this.size = size; cfg.setValue("backpacks." + id + ".size", size); - profile.markDirty(); + markDirty(); inventory = Bukkit.createInventory(null, size, "Backpack [" + size + " Slots]"); } - public int getID() { + /** + * This returns the id of this {@link PlayerBackpack} + * + * @return The id of this {@link PlayerBackpack} + */ + public int getId() { return id; } + /** + * This method returns the {@link PlayerProfile} this {@link PlayerBackpack} belongs to + * + * @return The owning {@link PlayerProfile} + */ + public PlayerProfile getOwner() { + return profile; + } + + /** + * This returns the size of this {@link PlayerBackpack}. + * + * @return The size of this {@link PlayerBackpack} + */ public int getSize() { return size; } + /** + * This method returns the {@link Inventory} of this {@link PlayerBackpack} + * + * @return The {@link Inventory} of this {@link PlayerBackpack} + */ public Inventory getInventory() { return inventory; } @@ -90,9 +119,11 @@ public class PlayerBackpack { * The players who this Backpack will be shown to */ public void open(Player... players) { - for (Player p : players) { - p.openInventory(inventory); - } + Slimefun.runSync(() -> { + for (Player p : players) { + p.openInventory(inventory); + } + }); } /** @@ -102,6 +133,10 @@ public class PlayerBackpack { * The new size for this Backpack */ public void setSize(int size) { + if (size < 9 || size > 54 || size % 9 != 0) { + throw new IllegalArgumentException("Invalid size! Size must be one of: [9, 18, 27, 36, 45, 54]"); + } + this.size = size; cfg.setValue("backpacks." + id + ".size", size); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java index e86ee00d5..ab027feac 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/player/PlayerProfile.java @@ -13,6 +13,7 @@ import java.util.UUID; import java.util.function.Consumer; import java.util.stream.IntStream; +import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; @@ -24,10 +25,10 @@ import io.github.thebusybiscuit.cscorelib2.chat.ChatColors; import io.github.thebusybiscuit.cscorelib2.config.Config; import io.github.thebusybiscuit.slimefun4.api.items.HashedArmorpiece; import io.github.thebusybiscuit.slimefun4.core.guide.GuideHistory; +import io.github.thebusybiscuit.slimefun4.core.researching.Research; import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import io.github.thebusybiscuit.slimefun4.utils.PatternUtils; import me.mrCookieSlime.Slimefun.SlimefunPlugin; -import me.mrCookieSlime.Slimefun.Objects.Research; /** * A class that can store a Player's {@link Research} progress for caching purposes. @@ -67,10 +68,6 @@ public final class PlayerProfile { } } - private PlayerProfile(UUID uuid) { - this(Bukkit.getOfflinePlayer(uuid)); - } - public HashedArmorpiece[] getArmor() { return armor; } @@ -79,6 +76,11 @@ public final class PlayerProfile { return cfg; } + /** + * This returns the {@link UUID} this {@link PlayerProfile} is linked to. + * + * @return The {@link UUID} of our {@link PlayerProfile} + */ public UUID getUUID() { return uuid; } @@ -124,6 +126,7 @@ public final class PlayerProfile { * Whether the {@link Research} should be unlocked or locked */ public void setResearched(Research research, boolean unlock) { + Validate.notNull(research, "Research must not be null!"); dirty = true; if (unlock) { @@ -144,6 +147,11 @@ public final class PlayerProfile { * @return Whether this {@link Research} has been unlocked */ public boolean hasUnlocked(Research research) { + if (research == null) { + // No Research, no restriction + return true; + } + return !research.isEnabled() || researches.contains(research); } @@ -181,15 +189,23 @@ public final class PlayerProfile { return backpack; } - public PlayerBackpack getBackpack(int id) { + public Optional getBackpack(int id) { + if (id < 0) { + throw new IllegalArgumentException("Backpacks cannot have negative ids!"); + } + PlayerBackpack backpack = backpacks.get(id); - if (backpack != null) return backpack; - else { + if (backpack != null) { + return Optional.of(backpack); + } + else if (cfg.contains("backpacks." + id + ".size")) { backpack = new PlayerBackpack(this, id); backpacks.put(id, backpack); - return backpack; + return Optional.of(backpack); } + + return Optional.empty(); } public String getTitle() { @@ -216,6 +232,12 @@ public final class PlayerProfile { sender.sendMessage(ChatColors.color("&7Total XP Levels spent: " + ChatColor.AQUA + levels)); } + /** + * This returns the {@link Player} who this {@link PlayerProfile} belongs to. + * If the {@link Player} is offline, null will be returned. + * + * @return The {@link Player} of this {@link PlayerProfile} or null + */ public Player getPlayer() { return Bukkit.getPlayer(getUUID()); } @@ -230,62 +252,8 @@ public final class PlayerProfile { return guideHistory; } - /** - * This is now deprecated, use {@link #fromUUID(UUID, Consumer)} instead - * - * @param uuid - * The UUID of the profile you are trying to retrieve. - * @return The PlayerProfile of this player - */ - public static PlayerProfile fromUUID(UUID uuid) { - PlayerProfile profile = SlimefunPlugin.getRegistry().getPlayerProfiles().get(uuid); - - if (profile == null) { - profile = new PlayerProfile(uuid); - SlimefunPlugin.getRegistry().getPlayerProfiles().put(uuid, profile); - } - else { - profile.markedForDeletion = false; - } - - return profile; - } - public static boolean fromUUID(UUID uuid, Consumer callback) { - PlayerProfile profile = SlimefunPlugin.getRegistry().getPlayerProfiles().get(uuid); - - if (profile != null) { - callback.accept(profile); - return true; - } - - Bukkit.getScheduler().runTaskAsynchronously(SlimefunPlugin.instance, () -> { - PlayerProfile pp = new PlayerProfile(uuid); - SlimefunPlugin.getRegistry().getPlayerProfiles().put(uuid, pp); - callback.accept(pp); - }); - return false; - } - - /** - * This is now deprecated, use {@link #get(OfflinePlayer, Consumer)} instead - * - * @param p - * The player's profile you wish to retrieve - * @return The PlayerProfile of this player - */ - public static PlayerProfile get(OfflinePlayer p) { - PlayerProfile profile = SlimefunPlugin.getRegistry().getPlayerProfiles().get(p.getUniqueId()); - - if (profile == null) { - profile = new PlayerProfile(p); - SlimefunPlugin.getRegistry().getPlayerProfiles().put(p.getUniqueId(), profile); - } - else { - profile.markedForDeletion = false; - } - - return profile; + return get(Bukkit.getOfflinePlayer(uuid), callback); } /** @@ -299,22 +267,56 @@ public final class PlayerProfile { * @return If the player was cached or not. */ public static boolean get(OfflinePlayer p, Consumer callback) { - PlayerProfile cached = SlimefunPlugin.getRegistry().getPlayerProfiles().get(p.getUniqueId()); + UUID uuid = p.getUniqueId(); + PlayerProfile profile = SlimefunPlugin.getRegistry().getPlayerProfiles().get(uuid); - if (cached != null) { - callback.accept(cached); + if (profile != null) { + callback.accept(profile); return true; } Bukkit.getScheduler().runTaskAsynchronously(SlimefunPlugin.instance, () -> { - PlayerProfile profile = new PlayerProfile(p); - SlimefunPlugin.getRegistry().getPlayerProfiles().put(p.getUniqueId(), profile); - callback.accept(profile); + PlayerProfile pp = new PlayerProfile(p); + SlimefunPlugin.getRegistry().getPlayerProfiles().put(uuid, pp); + callback.accept(pp); }); - + return false; } + /** + * This requests an instance of {@link PlayerProfile} to be loaded for the given {@link OfflinePlayer}. + * This method will return true if the {@link PlayerProfile} was already found. + * + * @param p + * The {@link OfflinePlayer} to request the {@link PlayerProfile} for. + * + * @return Whether the {@link PlayerProfile} was already loaded + */ + public static boolean request(OfflinePlayer p) { + if (!SlimefunPlugin.getRegistry().getPlayerProfiles().containsKey(p.getUniqueId())) { + // Should probably prevent multiple requests for the same profile in the future + Bukkit.getScheduler().runTaskAsynchronously(SlimefunPlugin.instance, () -> { + PlayerProfile pp = new PlayerProfile(p); + SlimefunPlugin.getRegistry().getPlayerProfiles().put(p.getUniqueId(), pp); + }); + + return false; + } + + return true; + } + + /** + * This method tries to search for a {@link PlayerProfile} of the given {@link OfflinePlayer}. + * The result of this method is an {@link Optional}, if no {@link PlayerProfile} was found, an empty + * {@link Optional} will be returned. + * + * @param p + * The {@link OfflinePlayer} to get the {@link PlayerProfile} for + * + * @return An {@link Optional} describing the result + */ public static Optional find(OfflinePlayer p) { return Optional.ofNullable(SlimefunPlugin.getRegistry().getPlayerProfiles().get(p.getUniqueId())); } @@ -323,8 +325,10 @@ public final class PlayerProfile { return SlimefunPlugin.getRegistry().getPlayerProfiles().values().iterator(); } - public static PlayerBackpack getBackpack(ItemStack item) { - if (item == null || !item.hasItemMeta() || !item.getItemMeta().hasLore()) return null; + public static void getBackpack(ItemStack item, Consumer callback) { + if (item == null || !item.hasItemMeta() || !item.getItemMeta().hasLore()) { + return; + } OptionalInt id = OptionalInt.empty(); String uuid = ""; @@ -341,14 +345,27 @@ public final class PlayerProfile { } if (id.isPresent()) { - PlayerProfile profile = fromUUID(UUID.fromString(uuid)); - return profile.getBackpack(id.getAsInt()); - } - else { - return null; + int number = id.getAsInt(); + fromUUID(UUID.fromString(uuid), profile -> { + Optional backpack = profile.getBackpack(number); + + if (backpack.isPresent()) { + callback.accept(backpack.get()); + } + }); } } + @Override + public int hashCode() { + return uuid.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof PlayerProfile && uuid.equals(((PlayerProfile) obj).uuid); + } + @Override public String toString() { return "PlayerProfile {" + uuid + "}"; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/MultiBlock.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/MultiBlock.java index 27d518f43..a374bbaf6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/MultiBlock.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/MultiBlock.java @@ -5,6 +5,7 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; +import org.apache.commons.lang.Validate; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.World; @@ -35,12 +36,14 @@ public class MultiBlock { private static final Set> SUPPORTED_TAGS = new HashSet<>(); static { - SUPPORTED_TAGS.add(Tag.LOGS); - SUPPORTED_TAGS.add(Tag.WOODEN_TRAPDOORS); - SUPPORTED_TAGS.add(Tag.WOODEN_SLABS); + if (SlimefunPlugin.getMinecraftVersion() != MinecraftVersion.UNIT_TEST) { + SUPPORTED_TAGS.add(Tag.LOGS); + SUPPORTED_TAGS.add(Tag.WOODEN_TRAPDOORS); + SUPPORTED_TAGS.add(Tag.WOODEN_SLABS); - if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) { - SUPPORTED_TAGS.add(Tag.WOODEN_FENCES); + if (SlimefunPlugin.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_14)) { + SUPPORTED_TAGS.add(Tag.WOODEN_FENCES); + } } } @@ -54,12 +57,17 @@ public class MultiBlock { private final boolean isSymmetric; public MultiBlock(SlimefunItem item, Material[] build, BlockFace trigger) { - this.item = item; + Validate.notNull(item, "A MultiBlock reuquires a SlimefunItem!"); + + if (build == null || build.length != 9) { + throw new IllegalArgumentException("MultiBlocks must have a length of 9!"); + } if (trigger != BlockFace.SELF && trigger != BlockFace.UP && trigger != BlockFace.DOWN) { throw new IllegalArgumentException("Multiblock Blockface must be either UP, DOWN or SELF"); } + this.item = item; this.blocks = build; this.trigger = trigger; this.isSymmetric = isSymmetric(build); @@ -89,7 +97,7 @@ public class MultiBlock { MultiBlock mb = (MultiBlock) obj; - if (trigger == mb.getTriggerBlock()) { + if (trigger == mb.getTriggerBlock() && isSymmetric == mb.isSymmetric) { for (int i = 0; i < mb.getStructure().length; i++) { if (!compareBlocks(blocks[i], mb.getStructure()[i])) { return false; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java index f40ea585b..de693fdf6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java @@ -24,11 +24,11 @@ import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; import io.github.thebusybiscuit.slimefun4.core.attributes.WitherProof; 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.implementation.guide.BookSlimefunGuide; import io.github.thebusybiscuit.slimefun4.implementation.guide.CheatSheetSlimefunGuide; import io.github.thebusybiscuit.slimefun4.implementation.guide.ChestSlimefunGuide; import me.mrCookieSlime.Slimefun.Objects.Category; -import me.mrCookieSlime.Slimefun.Objects.Research; import me.mrCookieSlime.Slimefun.Objects.SlimefunBlockHandler; import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem; import me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler; @@ -56,7 +56,6 @@ public class SlimefunRegistry { private final List researches = new LinkedList<>(); private final List researchRanks = new ArrayList<>(); private final Set researchingPlayers = new HashSet<>(); - private final KeyMap researchIds = new KeyMap<>(); private boolean automaticallyLoadItems; private boolean enableResearches; @@ -131,18 +130,10 @@ public class SlimefunRegistry { return enabledItems; } - public int countVanillaItems() { - return (int) getEnabledSlimefunItems().stream().filter(item -> !item.isAddonItem()).count(); - } - public List getResearches() { return researches; } - public KeyMap getResearchIds() { - return researchIds; - } - public Set 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 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]); + } + + } + +}