diff --git a/.github/configs/yaml-linter.yml b/.github/configs/yaml-linter.yml
index 90060a83c..3f056f2a1 100644
--- a/.github/configs/yaml-linter.yml
+++ b/.github/configs/yaml-linter.yml
@@ -6,10 +6,13 @@ yaml-files:
rules:
- ## A warning is sufficient here
- line-length:
- max: 180
- level: warning
+ ## Don't warn for line lengths
+ line-length: disable
+
+ truthy:
+ allowed-values: ['true', 'false']
+ ## We don't want it to trigger for the 'on' in our workflows
+ check-keys: false
## We don't need indentation warnings
indentation: disable
diff --git a/.github/workflows/url-checker.yml b/.github/workflows/url-checker.yml
index 523248f00..42483c750 100644
--- a/.github/workflows/url-checker.yml
+++ b/.github/workflows/url-checker.yml
@@ -6,7 +6,7 @@ on:
- master
jobs:
- build:
+ check:
name: URL Checker
runs-on: ubuntu-latest
@@ -17,7 +17,7 @@ jobs:
with:
git_path: https://github.com/Slimefun/Slimefun4
file_types: .md,.java,.yml
- print_all: False
+ print_all: false
retry_count: 2
## These URLs will always be correct, even if their services may be offline right now
white_listed_patterns: http://textures.minecraft.net/texture/,https://pastebin.com/,https://www.spigotmc.org/threads/spigot-bungeecord-1-16-1.447405/#post-3852349,https://gitlocalize.com/repo/3841
diff --git a/.github/workflows/yaml-linter.yml b/.github/workflows/yaml-linter.yml
index a1a4f8e88..2c34456f9 100644
--- a/.github/workflows/yaml-linter.yml
+++ b/.github/workflows/yaml-linter.yml
@@ -18,6 +18,6 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v2
- name: YAML Linter
- uses: ibiqlik/action-yamllint@v1.0.0
+ uses: ibiqlik/action-yamllint@v2.0.0
with:
config_file: '.github/configs/yaml-linter.yml'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed550b2c5..3bee33d01 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,12 @@
#### Changes
* Removed 1.13 support
+* Cooling Units can no longer be placed down
+* Heating Coils can no longer be placed down
+* Electric Motors can no longer be placed down
+* Cargo Motors can no longer be placed down
+* Magnets can no longer be placed down
+* Electromagnets can no longer be placed down
#### Fixes
* Fixed #2448
@@ -36,6 +42,11 @@
* Fixed #2478
* Fixed #2493
* Fixed a missing slot in the contributors menu
+* Fixed color codes in script downloading screen
+* Fixed #2505
+* Fixed contributors not showing correctly
+* Fixed #2469
+* Fixed #2509
## Release Candidate 17 (17 Oct 2020)
diff --git a/pom.xml b/pom.xml
index 6334ca848..0073cdbbc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -329,7 +329,7 @@
org.mockito
mockito-core
- 3.5.15
+ 3.6.0
test
@@ -381,7 +381,7 @@
com.gmail.nossr50.mcMMO
mcMMO
- 2.1.149
+ 2.1.150
provided
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 c9c577681..3e2ac719b 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
@@ -2,6 +2,9 @@ package io.github.thebusybiscuit.slimefun4.core.services;
import java.util.Optional;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
@@ -11,6 +14,9 @@ import org.bukkit.block.TileState;
import org.bukkit.persistence.PersistentDataHolder;
import org.bukkit.plugin.Plugin;
+import io.papermc.lib.PaperLib;
+import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult;
+
/**
* The {@link BlockDataService} is similar to the {@link CustomItemDataService},
* it is responsible for storing NBT data inside a {@link TileState}.
@@ -24,7 +30,17 @@ public class BlockDataService implements PersistentDataService, Keyed {
private final NamespacedKey namespacedKey;
- public BlockDataService(Plugin plugin, String key) {
+ /**
+ * This creates a new {@link BlockDataService} for the given {@link Plugin}.
+ * The {@link Plugin} and key will together form a {@link NamespacedKey} used to store
+ * data on a {@link TileState}.
+ *
+ * @param plugin
+ * The {@link Plugin} responsible for this service
+ * @param key
+ * The key under which to store data
+ */
+ public BlockDataService(@Nonnull Plugin plugin, @Nonnull String key) {
namespacedKey = new NamespacedKey(plugin, key);
}
@@ -41,12 +57,16 @@ public class BlockDataService implements PersistentDataService, Keyed {
* @param value
* The value to store
*/
- public void setBlockData(Block b, String value) {
- BlockState state = b.getState();
+ public void setBlockData(@Nonnull Block b, @Nonnull String value) {
+ BlockStateSnapshotResult result = PaperLib.getBlockState(b, false);
+ BlockState state = result.getState();
if (state instanceof TileState) {
setString((TileState) state, namespacedKey, value);
- state.update();
+
+ if (result.isSnapshot()) {
+ state.update();
+ }
}
}
@@ -57,8 +77,8 @@ public class BlockDataService implements PersistentDataService, Keyed {
* The {@link Block} to retrieve data from
* @return The stored value
*/
- public Optional getBlockData(Block b) {
- BlockState state = b.getState();
+ public Optional getBlockData(@Nonnull Block b) {
+ BlockState state = PaperLib.getBlockState(b, false).getState();
if (state instanceof TileState) {
return getString((TileState) state, namespacedKey);
@@ -77,9 +97,10 @@ public class BlockDataService implements PersistentDataService, Keyed {
*
* @param type
* The {@link Material} to check for
+ *
* @return Whether the given {@link Material} is considered a Tile Entity
*/
- public boolean isTileEntity(Material type) {
+ public boolean isTileEntity(@Nullable Material type) {
if (type == null || type.isAir()) {
// Cannot store data on air
return false;
@@ -104,8 +125,10 @@ public class BlockDataService implements PersistentDataService, Keyed {
case BARREL:
case SPAWNER:
case BEACON:
+ // All of the above Materials are Tile Entities
return true;
default:
+ // Otherwise we assume they're not Tile Entities
return false;
}
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomItemDataService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomItemDataService.java
index b2ca07a7c..aed4e5d3e 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomItemDataService.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/CustomItemDataService.java
@@ -2,6 +2,10 @@ package io.github.thebusybiscuit.slimefun4.core.services;
import java.util.Optional;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.commons.lang.Validate;
import org.bukkit.Keyed;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
@@ -35,7 +39,7 @@ public class CustomItemDataService implements PersistentDataService, Keyed {
* @param key
* The key under which to store data
*/
- public CustomItemDataService(Plugin plugin, String key) {
+ public CustomItemDataService(@Nonnull Plugin plugin, @Nonnull String key) {
// Null-Validation is performed in the NamespacedKey constructor
namespacedKey = new NamespacedKey(plugin, key);
}
@@ -45,13 +49,37 @@ public class CustomItemDataService implements PersistentDataService, Keyed {
return namespacedKey;
}
- public void setItemData(ItemStack item, String id) {
+ /**
+ * This method stores the given id on the provided {@link ItemStack} via
+ * persistent data.
+ *
+ * @param item
+ * The {@link ItemStack} to store data on
+ * @param id
+ * The id to store on the {@link ItemStack}
+ */
+ public void setItemData(@Nonnull ItemStack item, @Nonnull String id) {
+ Validate.notNull(item, "The Item cannot be null!");
+ Validate.notNull(id, "Cannot store null on an Item!");
+
ItemMeta im = item.getItemMeta();
setItemData(im, id);
item.setItemMeta(im);
}
- public void setItemData(ItemMeta im, String id) {
+ /**
+ * This method stores the given id on the provided {@link ItemMeta} via
+ * persistent data.
+ *
+ * @param im
+ * The {@link ItemMeta} to store data on
+ * @param id
+ * The id to store on the {@link ItemMeta}
+ */
+ public void setItemData(@Nonnull ItemMeta im, @Nonnull String id) {
+ Validate.notNull(im, "The ItemMeta cannot be null!");
+ Validate.notNull(id, "Cannot store null on an ItemMeta!");
+
setString(im, namespacedKey, id);
}
@@ -65,7 +93,8 @@ public class CustomItemDataService implements PersistentDataService, Keyed {
*
* @return An {@link Optional} describing the result
*/
- public Optional getItemData(ItemStack item) {
+ @Nonnull
+ public Optional getItemData(@Nullable ItemStack item) {
if (item == null || item.getType() == Material.AIR || !item.hasItemMeta()) {
return Optional.empty();
}
@@ -82,7 +111,10 @@ public class CustomItemDataService implements PersistentDataService, Keyed {
*
* @return An {@link Optional} describing the result
*/
- public Optional getItemData(ItemMeta meta) {
+ @Nonnull
+ public Optional getItemData(@Nonnull ItemMeta meta) {
+ Validate.notNull(meta, "Cannot read data from null!");
+
return getString(meta, namespacedKey);
}
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 5587ad8b2..3caea1cbb 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
@@ -28,6 +28,12 @@ public class CustomTextureService {
private String version = null;
private boolean modified = false;
+ /**
+ * This creates a new {@link CustomTextureService} for the provided {@link Config}
+ *
+ * @param config
+ * The {@link Config} to read custom model data from
+ */
public CustomTextureService(@Nonnull 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.");
@@ -49,6 +55,8 @@ public class CustomTextureService {
config.setDefaultValue("SLIMEFUN_GUIDE", 0);
config.setDefaultValue("_UI_BACKGROUND", 0);
+ config.setDefaultValue("_UI_NO_PERMISSION", 0);
+ config.setDefaultValue("_UI_NOT_RESEARCHED", 0);
config.setDefaultValue("_UI_INPUT_SLOT", 0);
config.setDefaultValue("_UI_OUTPUT_SLOT", 0);
config.setDefaultValue("_UI_BACK", 0);
@@ -82,22 +90,60 @@ public class CustomTextureService {
return version;
}
+ /**
+ * This returns true if any custom model data was configured.
+ * If every item id has no configured custom model data, it will return false.
+ *
+ * @return Whether any custom model data was configured
+ */
public boolean isActive() {
return modified;
}
+ /**
+ * This returns the configured custom model data for a given id.
+ *
+ * @param id
+ * The id to get the data for
+ *
+ * @return The configured custom model data
+ */
public int getModelData(@Nonnull String id) {
Validate.notNull(id, "Cannot get the ModelData for 'null'");
return config.getInt(id);
}
+ /**
+ * This method sets the custom model data for this {@link ItemStack}
+ * to the value configured for the provided item id.
+ *
+ * @param item
+ * The {@link ItemStack} to set the custom model data for
+ * @param id
+ * The id for which to get the configured model data
+ */
public void setTexture(@Nonnull ItemStack item, @Nonnull String id) {
+ Validate.notNull(item, "The Item cannot be null!");
+ Validate.notNull(id, "Cannot store null on an Item!");
+
ItemMeta im = item.getItemMeta();
setTexture(im, id);
item.setItemMeta(im);
}
+ /**
+ * This method sets the custom model data for this {@link ItemMeta}
+ * to the value configured for the provided item id.
+ *
+ * @param im
+ * The {@link ItemMeta} to set the custom model data for
+ * @param id
+ * The id for which to get the configured model data
+ */
public void setTexture(@Nonnull ItemMeta im, @Nonnull String id) {
+ Validate.notNull(im, "The ItemMeta cannot be null!");
+ Validate.notNull(id, "Cannot store null on an ItemMeta!");
+
int data = getModelData(id);
im.setCustomModelData(data == 0 ? null : data);
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java
index 5835e9f86..74ddcd2e1 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/MetricsService.java
@@ -34,9 +34,26 @@ import kong.unirest.UnirestException;
*/
public class MetricsService {
+ /**
+ * The URL pointing towards the GitHub API.
+ */
private static final String API_URL = "https://api.github.com/";
+
+ /**
+ * The Name of our repository
+ */
private static final String REPO_NAME = "MetricsModule";
+
+ /**
+ * The URL pointing towards the /releases/ endpoint of our
+ * Metrics repository
+ */
private static final String RELEASES_URL = API_URL + "repos/Slimefun/" + REPO_NAME + "/releases/latest";
+
+ /**
+ * The URL pointing towards the download location for a
+ * GitHub release of our Metrics repository
+ */
private static final String DOWNLOAD_URL = "https://github.com/Slimefun/" + REPO_NAME + "/releases/download";
private final SlimefunPlugin plugin;
@@ -48,9 +65,22 @@ public class MetricsService {
private boolean hasDownloadedUpdate = false;
static {
- Unirest.config().concurrency(2, 1).setDefaultHeader("User-Agent", "MetricsModule Auto-Updater").setDefaultHeader("Accept", "application/vnd.github.v3+json").enableCookieManagement(false).cookieSpec("ignoreCookies");
+ // @formatter:off (We want this to stay this nicely aligned :D )
+ Unirest.config()
+ .concurrency(2, 1)
+ .setDefaultHeader("User-Agent", "MetricsModule Auto-Updater")
+ .setDefaultHeader("Accept", "application/vnd.github.v3+json")
+ .enableCookieManagement(false)
+ .cookieSpec("ignoreCookies");
+ // @formatter:on
}
+ /**
+ * This constructs a new instance of our {@link MetricsService}.
+ *
+ * @param plugin
+ * Our {@link SlimefunPlugin} instance
+ */
public MetricsService(@Nonnull SlimefunPlugin plugin) {
this.plugin = plugin;
this.parentFolder = new File(plugin.getDataFolder(), "cache" + File.separatorChar + "modules");
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java
index f38e0c035..3d2368cea 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/ContributionsConnector.java
@@ -16,11 +16,28 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
class ContributionsConnector extends GitHubConnector {
- // GitHub Bots that do not count as Contributors
- // (includes "invalid-email-address" because it is an invalid contributor)
- private static final List blacklist = Arrays.asList("invalid-email-address", "renovate-bot", "TheBusyBot", "ImgBotApp", "imgbot", "imgbot[bot]", "github-actions[bot]", "gitlocalize-app", "gitlocalize-app[bot]", "mt-gitlocalize");
+ /*
+ * @formatter:off
+ * GitHub Bots that do not count as Contributors
+ * (includes "invalid-email-address" because it is an invalid contributor)
+ */
+ private static final List blacklist = Arrays.asList(
+ "invalid-email-address",
+ "renovate-bot",
+ "TheBusyBot",
+ "ImgBotApp",
+ "imgbot",
+ "imgbot[bot]",
+ "github-actions[bot]",
+ "gitlocalize-app",
+ "gitlocalize-app[bot]",
+ "mt-gitlocalize"
+ );
- // Matches a GitHub name with a Minecraft name.
+ /*
+ * @formatter:on
+ * Matches a GitHub name with a Minecraft name.
+ */
private static final Map aliases = new HashMap<>();
// Should probably be switched to UUIDs at some point...
@@ -82,8 +99,16 @@ class ContributionsConnector extends GitHubConnector {
}
@Override
- public String getURLSuffix() {
- return "/contributors?per_page=100&page=" + page;
+ public String getEndpoint() {
+ return "/contributors";
+ }
+
+ @Override
+ public Map getParameters() {
+ Map parameters = new HashMap<>();
+ parameters.put("per_page", 100);
+ parameters.put("page", page);
+ return parameters;
}
private void computeContributors(@Nonnull JSONArray array) {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubActivityConnector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubActivityConnector.java
index e55fc722e..33b6d23e1 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubActivityConnector.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubActivityConnector.java
@@ -1,6 +1,8 @@
package io.github.thebusybiscuit.slimefun4.core.services.github;
import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
@@ -35,8 +37,13 @@ class GitHubActivityConnector extends GitHubConnector {
}
@Override
- public String getURLSuffix() {
+ public String getEndpoint() {
return "";
}
+ @Override
+ public Map getParameters() {
+ return new HashMap<>();
+ }
+
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubConnector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubConnector.java
index 0f9e3e317..e06e00129 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubConnector.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubConnector.java
@@ -6,12 +6,13 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.net.URL;
import java.nio.charset.StandardCharsets;
+import java.util.Map;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import javax.annotation.ParametersAreNonnullByDefault;
import kong.unirest.HttpResponse;
import kong.unirest.JsonNode;
@@ -31,22 +32,49 @@ import me.mrCookieSlime.Slimefun.api.Slimefun;
abstract class GitHubConnector {
private static final String API_URL = "https://api.github.com/";
+ private static final String USER_AGENT = "Slimefun4 (https://github.com/Slimefun)";
- protected File file;
- protected String repository;
protected final GitHubService github;
+ private final String url;
+ private File file;
- @ParametersAreNonnullByDefault
- public GitHubConnector(GitHubService github, String repository) {
+ /**
+ * This creates a new {@link GitHubConnector} for the given repository.
+ *
+ * @param github
+ * Our instance of {@link GitHubService}
+ * @param repository
+ * The repository we want to connect to
+ */
+ GitHubConnector(@Nonnull GitHubService github, @Nonnull String repository) {
this.github = github;
- this.repository = repository;
+ this.url = API_URL + "repos/" + repository + getEndpoint();
}
+ /**
+ * This returns the name of our cache {@link File}.
+ *
+ * @return The cache {@link File} name
+ */
@Nonnull
public abstract String getFileName();
+ /**
+ * This is our {@link URL} endpoint.
+ * It is the suffix of the {@link URL} we want to connect to.
+ *
+ * @return Our endpoint
+ */
@Nonnull
- public abstract String getURLSuffix();
+ public abstract String getEndpoint();
+
+ /**
+ * This {@link Map} contains the query parameters for our {@link URL}.
+ *
+ * @return A {@link Map} with our query parameters
+ */
+ @Nonnull
+ public abstract Map getParameters();
/**
* This method is called when the connection finished successfully.
@@ -63,7 +91,12 @@ abstract class GitHubConnector {
// Don't do anything by default
}
- public void pullFile() {
+ /**
+ * This method will connect to GitHub and store the received data inside a local
+ * cache {@link File}.
+ * Make sure to call this method asynchronously!
+ */
+ void download() {
file = new File("plugins/Slimefun/cache/github/" + getFileName() + ".json");
if (github.isLoggingEnabled()) {
@@ -71,16 +104,19 @@ abstract class GitHubConnector {
}
try {
- HttpResponse resp = Unirest.get(API_URL + "repos/" + repository + getURLSuffix())
- .header("User-Agent", "Slimefun4 (https://github.com/Slimefun)")
+ // @formatter:off
+ HttpResponse response = Unirest.get(url)
+ .queryString(getParameters())
+ .header("User-Agent", USER_AGENT)
.asJson();
+ // @formatter:on
- if (resp.isSuccess()) {
- onSuccess(resp.getBody());
- writeCacheFile(resp.getBody());
+ if (response.isSuccess()) {
+ onSuccess(response.getBody());
+ writeCacheFile(response.getBody());
} else {
if (github.isLoggingEnabled()) {
- Slimefun.getLogger().log(Level.WARNING, "Failed to fetch {0}: {1} - {2}", new Object[] { repository + getURLSuffix(), resp.getStatus(), resp.getBody() });
+ Slimefun.getLogger().log(Level.WARNING, "Failed to fetch {0}: {1} - {2}", new Object[] { url, response.getStatus(), response.getBody() });
}
// It has the cached file, let's just read that then
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubIssuesConnector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubIssuesConnector.java
index b22d9f491..38335e529 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubIssuesConnector.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubIssuesConnector.java
@@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.core.services.github;
+import java.util.HashMap;
+import java.util.Map;
import java.util.logging.Level;
import javax.annotation.Nonnull;
@@ -50,8 +52,15 @@ class GitHubIssuesConnector extends GitHubConnector {
}
@Override
- public String getURLSuffix() {
- return "/issues?per_page=100";
+ public String getEndpoint() {
+ return "/issues";
+ }
+
+ @Override
+ public Map getParameters() {
+ Map parameters = new HashMap<>();
+ parameters.put("per_page", 100);
+ return parameters;
}
}
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 b7a2fc07d..f7d103f24 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
@@ -57,7 +57,8 @@ public class GitHubService {
}
public void start(@Nonnull SlimefunPlugin plugin) {
- plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, new GitHubTask(this), 80L, 60 * 60 * 20L);
+ GitHubTask task = new GitHubTask(this);
+ plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, task, 80L, 60 * 60 * 20L);
}
/**
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java
index 932bf7060..151afa635 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java
@@ -1,5 +1,6 @@
package io.github.thebusybiscuit.slimefun4.core.services.github;
+import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -38,10 +39,14 @@ class GitHubTask implements Runnable {
@Override
public void run() {
- gitHubService.getConnectors().forEach(GitHubConnector::pullFile);
+ gitHubService.getConnectors().forEach(GitHubConnector::download);
grabTextures();
}
+ /**
+ * This method will pull the skin textures for every {@link Contributor} and store
+ * the {@link UUID} and received skin inside a local cache {@link File}.
+ */
private void grabTextures() {
// Store all queried usernames to prevent 429 responses for pinging the
// same URL twice in one run.
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 e024b4c38..d88a295bf 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
@@ -271,10 +271,10 @@ public class ChestSlimefunGuide implements SlimefunGuideImplementation {
if (isSurvivalMode() && !Slimefun.hasPermission(p, sfitem, false)) {
List message = SlimefunPlugin.getPermissionsService().getLore(sfitem);
- menu.addItem(index, new CustomItem(Material.BARRIER, sfitem.getItemName(), message.toArray(new String[0])));
+ menu.addItem(index, new CustomItem(ChestMenuUtils.getNoPermissionItem(), sfitem.getItemName(), message.toArray(new String[0])));
menu.addMenuClickHandler(index, ChestMenuUtils.getEmptyClickHandler());
} else if (isSurvivalMode() && research != null && !profile.hasUnlocked(research)) {
- menu.addItem(index, new CustomItem(Material.BARRIER, ChatColor.WHITE + ItemUtils.getItemName(sfitem.getItem()), "&4&l" + SlimefunPlugin.getLocalization().getMessage(p, "guide.locked"), "", "&a> Click to unlock", "", "&7Cost: &b" + research.getCost() + " Level(s)"));
+ menu.addItem(index, new CustomItem(ChestMenuUtils.getNotResearchedItem(), ChatColor.WHITE + ItemUtils.getItemName(sfitem.getItem()), "&4&l" + SlimefunPlugin.getLocalization().getMessage(p, "guide.locked"), "", "&a> Click to unlock", "", "&7Cost: &b" + research.getCost() + " Level(s)"));
menu.addMenuClickHandler(index, (pl, slot, item, action) -> {
if (!SlimefunPlugin.getRegistry().getCurrentlyResearchingPlayers().contains(pl.getUniqueId())) {
if (research.canUnlock(pl)) {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidType.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidType.java
index 61e7a2eaa..732c998a3 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidType.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/AndroidType.java
@@ -1,5 +1,7 @@
package io.github.thebusybiscuit.slimefun4.implementation.items.androids;
+import javax.annotation.Nonnull;
+
/**
* This enum holds all the different types a {@link ProgrammableAndroid} can represent.
*
@@ -55,7 +57,7 @@ public enum AndroidType {
*/
NON_FIGHTER;
- boolean isType(AndroidType type) {
+ boolean isType(@Nonnull AndroidType type) {
return type == NONE || type == this || (type == NON_FIGHTER && this != FIGHTER);
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java
index 4a2083b05..6c707c979 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Instruction.java
@@ -8,6 +8,7 @@ import java.util.Map;
import java.util.function.Predicate;
import org.apache.commons.lang.Validate;
+import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Ageable;
@@ -20,126 +21,223 @@ import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
-enum Instruction {
+/**
+ * This enum holds every {@link Instruction} for the {@link ProgrammableAndroid}
+ * added by Slimefun itself.
+ *
+ * @author TheBusyBiscuit
+ *
+ */
+public enum Instruction {
- // Start and End Parts
+ /**
+ * This {@link Instruction} is the starting point of a {@link Script}.
+ */
START(AndroidType.NONE, HeadTexture.SCRIPT_START),
+
+ /**
+ * This {@link Instruction} is the end token of a {@link Script}.
+ * Once this {@link Instruction} is reached, the {@link Script} will start again.
+ */
REPEAT(AndroidType.NONE, HeadTexture.SCRIPT_REPEAT),
+
+ /**
+ * This {@link Instruction} will make the {@link ProgrammableAndroid} wait
+ * for one Slimefun tick.
+ */
WAIT(AndroidType.NONE, HeadTexture.SCRIPT_WAIT),
- // Movement
+ /**
+ * This will make the {@link ProgrammableAndroid} go forward.
+ */
GO_FORWARD(AndroidType.NON_FIGHTER, HeadTexture.SCRIPT_FORWARD, (android, b, inv, face) -> {
Block target = b.getRelative(face);
android.move(b, face, target);
}),
+ /**
+ * This will make the {@link ProgrammableAndroid} go up.
+ */
GO_UP(AndroidType.NON_FIGHTER, HeadTexture.SCRIPT_UP, (android, b, inv, face) -> {
Block target = b.getRelative(BlockFace.UP);
android.move(b, face, target);
}),
+ /**
+ * This will make the {@link ProgrammableAndroid} go down.
+ */
GO_DOWN(AndroidType.NON_FIGHTER, HeadTexture.SCRIPT_DOWN, (android, b, inv, face) -> {
Block target = b.getRelative(BlockFace.DOWN);
android.move(b, face, target);
}),
- // Directions
+ /**
+ * This will make the {@link ProgrammableAndroid} rotate to the left side.
+ */
TURN_LEFT(AndroidType.NONE, HeadTexture.SCRIPT_LEFT, (android, b, inv, face) -> {
int mod = -1;
android.rotate(b, face, mod);
}),
+ /**
+ * This will make the {@link ProgrammableAndroid} rotate to the right side.
+ */
TURN_RIGHT(AndroidType.NONE, HeadTexture.SCRIPT_RIGHT, (android, b, inv, face) -> {
int mod = 1;
android.rotate(b, face, mod);
}),
- // Action - Pickaxe
+ /**
+ * This will make a {@link MinerAndroid} dig the {@link Block} above.
+ */
DIG_UP(AndroidType.MINER, HeadTexture.SCRIPT_DIG_UP, (android, b, inv, face) -> {
Block target = b.getRelative(BlockFace.UP);
android.dig(b, inv, target);
}),
+ /**
+ * This will make a {@link MinerAndroid} dig the {@link Block} ahead.
+ */
DIG_FORWARD(AndroidType.MINER, HeadTexture.SCRIPT_DIG_FORWARD, (android, b, inv, face) -> {
Block target = b.getRelative(face);
android.dig(b, inv, target);
}),
+ /**
+ * This will make a {@link MinerAndroid} dig the {@link Block} below.
+ */
DIG_DOWN(AndroidType.MINER, HeadTexture.SCRIPT_DIG_DOWN, (android, b, inv, face) -> {
Block target = b.getRelative(BlockFace.DOWN);
android.dig(b, inv, target);
}),
+ /**
+ * This will make a {@link MinerAndroid} dig the {@link Block} above
+ * and then move itself to that new {@link Location}.
+ */
MOVE_AND_DIG_UP(AndroidType.MINER, HeadTexture.SCRIPT_DIG_UP, (android, b, inv, face) -> {
Block target = b.getRelative(BlockFace.UP);
android.moveAndDig(b, inv, face, target);
}),
+ /**
+ * This will make a {@link MinerAndroid} dig the {@link Block} ahead
+ * and then move itself to that new {@link Location}.
+ */
MOVE_AND_DIG_FORWARD(AndroidType.MINER, HeadTexture.SCRIPT_DIG_FORWARD, (android, b, inv, face) -> {
Block target = b.getRelative(face);
android.moveAndDig(b, inv, face, target);
}),
+ /**
+ * This will make a {@link MinerAndroid} dig the {@link Block} below
+ * and then move itself to that new {@link Location}.
+ */
MOVE_AND_DIG_DOWN(AndroidType.MINER, HeadTexture.SCRIPT_DIG_DOWN, (android, b, inv, face) -> {
Block target = b.getRelative(BlockFace.DOWN);
android.moveAndDig(b, inv, face, target);
}),
- // Action - Sword
+ /**
+ * This will make a {@link ButcherAndroid} attack any {@link LivingEntity}
+ * ahead of them.
+ */
ATTACK_MOBS_ANIMALS(AndroidType.FIGHTER, HeadTexture.SCRIPT_ATTACK, (android, b, inv, face) -> {
Predicate predicate = e -> true;
android.attack(b, face, predicate);
}),
+ /**
+ * This will make a {@link ButcherAndroid} attack any {@link Monster}
+ * ahead of them.
+ */
ATTACK_MOBS(AndroidType.FIGHTER, HeadTexture.SCRIPT_ATTACK, (android, b, inv, face) -> {
Predicate predicate = e -> e instanceof Monster;
android.attack(b, face, predicate);
}),
+ /**
+ * This will make a {@link ButcherAndroid} attack any {@link Animals Animal}
+ * ahead of them.
+ */
ATTACK_ANIMALS(AndroidType.FIGHTER, HeadTexture.SCRIPT_ATTACK, (android, b, inv, face) -> {
Predicate predicate = e -> e instanceof Animals;
android.attack(b, face, predicate);
}),
+ /**
+ * This will make a {@link ButcherAndroid} attack any adult
+ * {@link Animals Animal} ahead of them.
+ */
ATTACK_ANIMALS_ADULT(AndroidType.FIGHTER, HeadTexture.SCRIPT_ATTACK, (android, b, inv, face) -> {
- Predicate predicate = e -> e instanceof Animals && e instanceof Ageable && ((Ageable) e).isAdult();
+ Predicate predicate = e -> e instanceof Animals && ((Ageable) e).isAdult();
android.attack(b, face, predicate);
}),
- // Action - Axe
+ /**
+ * This will make a {@link WoodcutterAndroid} chop down the tree in front of them.
+ */
CHOP_TREE(AndroidType.WOODCUTTER, HeadTexture.SCRIPT_CHOP_TREE),
- // Action - Fishing Rod
+ /**
+ * This {@link Instruction} makes a {@link FisherAndroid} try to catch fish from
+ * the water below.
+ */
CATCH_FISH(AndroidType.FISHERMAN, HeadTexture.SCRIPT_FISH, (android, b, inv, face) -> android.fish(b, inv)),
- // Action - Hoe
+ /**
+ * This {@link Instruction} will make a {@link FarmerAndroid} try to harvest
+ * the {@link Block} in front of them.
+ */
FARM_FORWARD(AndroidType.FARMER, HeadTexture.SCRIPT_FARM_FORWARD, (android, b, inv, face) -> {
Block target = b.getRelative(face);
android.farm(inv, target);
}),
+ /**
+ * This {@link Instruction} will make a {@link FarmerAndroid} try to harvest
+ * the {@link Block} below.
+ */
FARM_DOWN(AndroidType.FARMER, HeadTexture.SCRIPT_FARM_DOWN, (android, b, inv, face) -> {
Block target = b.getRelative(BlockFace.DOWN);
android.farm(inv, target);
}),
- // Action - ExoticGarden
+ /**
+ * This {@link Instruction} will make a {@link FarmerAndroid} try to harvest
+ * the {@link Block} in front of them.
+ *
+ * This includes plants from ExoticGarden.
+ */
FARM_EXOTIC_FORWARD(AndroidType.ADVANCED_FARMER, HeadTexture.SCRIPT_FARM_FORWARD, (android, b, inv, face) -> {
Block target = b.getRelative(face);
android.exoticFarm(inv, target);
}),
+ /**
+ * This {@link Instruction} will make a {@link FarmerAndroid} try to harvest
+ * the {@link Block} below.
+ *
+ * This includes plants from ExoticGarden.
+ */
FARM_EXOTIC_DOWN(AndroidType.ADVANCED_FARMER, HeadTexture.SCRIPT_FARM_DOWN, (android, b, inv, face) -> {
Block target = b.getRelative(BlockFace.DOWN);
android.exoticFarm(inv, target);
}),
- // Action - Interface
+ /**
+ * This {@link Instruction} will force the {@link ProgrammableAndroid} to push their
+ * items into an {@link AndroidInterface} ahead of them.
+ */
INTERFACE_ITEMS(AndroidType.NONE, HeadTexture.SCRIPT_PUSH_ITEMS, (android, b, inv, face) -> {
Block target = b.getRelative(face);
android.depositItems(inv, target);
}),
+ /**
+ * This {@link Instruction} will force the {@link ProgrammableAndroid} to pull
+ * fuel from an {@link AndroidInterface} ahead of them.
+ */
INTERFACE_FUEL(AndroidType.NONE, HeadTexture.SCRIPT_PULL_FUEL, (android, b, inv, face) -> {
Block target = b.getRelative(face);
android.refuel(inv, target);
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java
index b57b449e4..07d828f96 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java
@@ -58,6 +58,7 @@ import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.interfaces.InventoryBlock;
import me.mrCookieSlime.Slimefun.Objects.handlers.BlockTicker;
import me.mrCookieSlime.Slimefun.Objects.handlers.ItemHandler;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
+import me.mrCookieSlime.Slimefun.api.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset;
@@ -69,6 +70,7 @@ public class ProgrammableAndroid extends SlimefunItem implements InventoryBlock,
private static final int[] BORDER = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 18, 24, 25, 26, 27, 33, 35, 36, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53 };
private static final int[] OUTPUT_BORDER = { 10, 11, 12, 13, 14, 19, 23, 28, 32, 37, 38, 39, 40, 41 };
private static final String DEFAULT_SCRIPT = "START-TURN_LEFT-REPEAT";
+ private static final int MAX_SCRIPT_LENGTH = 54;
protected final List fuelTypes = new ArrayList<>();
protected final String texture;
@@ -407,19 +409,23 @@ public class ProgrammableAndroid extends SlimefunItem implements InventoryBlock,
} else {
Script script = scripts.get(target);
menu.addItem(index, script.getAsItemStack(this, p), (player, slot, stack, action) -> {
- if (action.isShiftClicked()) {
- if (script.isAuthor(player)) {
- SlimefunPlugin.getLocalization().sendMessage(player, "android.scripts.rating.own", true);
- } else if (script.canRate(player)) {
- script.rate(player, !action.isRightClicked());
- openScriptDownloader(player, b, page);
- } else {
- SlimefunPlugin.getLocalization().sendMessage(player, "android.scripts.rating.already", true);
+ try {
+ if (action.isShiftClicked()) {
+ if (script.isAuthor(player)) {
+ SlimefunPlugin.getLocalization().sendMessage(player, "android.scripts.rating.own", true);
+ } else if (script.canRate(player)) {
+ script.rate(player, !action.isRightClicked());
+ openScriptDownloader(player, b, page);
+ } else {
+ SlimefunPlugin.getLocalization().sendMessage(player, "android.scripts.rating.already", true);
+ }
+ } else if (!action.isRightClicked()) {
+ script.download();
+ setScript(b.getLocation(), script.getSourceCode());
+ openScriptEditor(player, b);
}
- } else if (!action.isRightClicked()) {
- script.download();
- setScript(b.getLocation(), script.getSourceCode());
- openScriptEditor(player, b);
+ } catch (Exception x) {
+ Slimefun.getLogger().log(Level.SEVERE, "An Exception was thrown when a User tried to download a Script!", x);
}
return false;
@@ -534,12 +540,19 @@ public class ProgrammableAndroid extends SlimefunItem implements InventoryBlock,
}
@Nonnull
- protected String getScript(@Nonnull Location l) {
+ public String getScript(@Nonnull Location l) {
+ Validate.notNull(l, "Location for android not specified");
String script = BlockStorage.getLocationInfo(l, "script");
return script != null ? script : DEFAULT_SCRIPT;
}
- protected void setScript(@Nonnull Location l, @Nonnull String script) {
+ public void setScript(@Nonnull Location l, @Nonnull String script) {
+ Validate.notNull(l, "Location for android not specified");
+ Validate.notNull(script, "No script given");
+ Validate.isTrue(script.startsWith(Instruction.START.name() + '-'), "A script must begin with a 'START' token.");
+ Validate.isTrue(script.endsWith('-' + Instruction.REPEAT.name()), "A script must end with a 'REPEAT' token.");
+ Validate.isTrue(PatternUtils.DASH.split(script).length <= MAX_SCRIPT_LENGTH, "Scripts may not have more than " + MAX_SCRIPT_LENGTH + " segments");
+
BlockStorage.addBlockInfo(l, "script", script);
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Script.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Script.java
index 5d97975f7..7ee370638 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Script.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/Script.java
@@ -130,18 +130,19 @@ public final class Script {
@Nonnull
ItemStack getAsItemStack(@Nonnull ProgrammableAndroid android, @Nonnull Player p) {
List lore = new LinkedList<>();
- lore.add("&7by &r" + getAuthor());
+ lore.add("&7by &f" + getAuthor());
lore.add("");
- lore.add("&7Downloads: &r" + getDownloads());
+ lore.add("&7Downloads: &f" + getDownloads());
lore.add("&7Rating: " + getScriptRatingPercentage());
lore.add("&a" + getUpvotes() + " \u263A &7| &4\u2639 " + getDownvotes());
lore.add("");
- lore.add("&eLeft Click &rto download this Script");
+ lore.add("&eLeft Click &fto download this Script");
lore.add("&4(This will override your current Script)");
if (canRate(p)) {
- lore.add("&eShift + Left Click &rto leave a positive Rating");
- lore.add("&eShift + Right Click &rto leave a negative Rating");
+ lore.add("");
+ lore.add("&eShift + Left Click &fto leave a positive Rating");
+ lore.add("&eShift + Right Click &fto leave a negative Rating");
}
return new CustomItem(android.getItem(), "&b" + getName(), lore.toArray(new String[0]));
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/ArmorForge.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/ArmorForge.java
index 1cb865b36..608a0e2f5 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/ArmorForge.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/ArmorForge.java
@@ -2,6 +2,8 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks;
import java.util.List;
+import javax.annotation.ParametersAreNonnullByDefault;
+
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.block.Block;
@@ -14,7 +16,6 @@ import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.cscorelib2.inventory.ItemUtils;
import io.github.thebusybiscuit.cscorelib2.item.CustomItem;
-import io.github.thebusybiscuit.slimefun4.core.multiblocks.MultiBlockMachine;
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.papermc.lib.PaperLib;
@@ -23,7 +24,7 @@ import me.mrCookieSlime.Slimefun.Objects.Category;
import me.mrCookieSlime.Slimefun.api.Slimefun;
import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
-public class ArmorForge extends MultiBlockMachine {
+public class ArmorForge extends BackpackCrafter {
public ArmorForge(Category category, SlimefunItemStack item) {
super(category, item, new ItemStack[] { null, null, null, null, new ItemStack(Material.ANVIL), null, null, new CustomItem(Material.DISPENSER, "Dispenser (Facing up)"), null }, BlockFace.SELF);
@@ -31,8 +32,8 @@ public class ArmorForge extends MultiBlockMachine {
@Override
public void onInteract(Player p, Block b) {
- Block dispBlock = b.getRelative(BlockFace.DOWN);
- BlockState state = PaperLib.getBlockState(dispBlock, false).getState();
+ Block dispenser = b.getRelative(BlockFace.DOWN);
+ BlockState state = PaperLib.getBlockState(dispenser, false).getState();
if (state instanceof Dispenser) {
Dispenser disp = (Dispenser) state;
@@ -44,13 +45,7 @@ public class ArmorForge extends MultiBlockMachine {
ItemStack output = RecipeType.getRecipeOutputList(this, inputs.get(i)).clone();
if (Slimefun.hasUnlocked(p, output, true)) {
- Inventory outputInv = findOutputInventory(output, dispBlock, inv);
-
- if (outputInv != null) {
- craft(p, output, inv, outputInv);
- } else {
- SlimefunPlugin.getLocalization().sendMessage(p, "machines.full-inventory", true);
- }
+ craft(p, output, inv, dispenser);
}
return;
@@ -71,26 +66,35 @@ public class ArmorForge extends MultiBlockMachine {
return true;
}
- private void craft(Player p, ItemStack output, Inventory inv, Inventory outputInv) {
- for (int j = 0; j < 9; j++) {
- ItemStack item = inv.getContents()[j];
+ @ParametersAreNonnullByDefault
+ private void craft(Player p, ItemStack output, Inventory inv, Block dispenser) {
+ Inventory fakeInv = createVirtualInventory(inv);
+ Inventory outputInv = findOutputInventory(output, dispenser, inv, fakeInv);
- if (item != null && item.getType() != Material.AIR) {
- ItemUtils.consumeItem(item, true);
- }
- }
+ if (outputInv != null) {
+ for (int j = 0; j < 9; j++) {
+ ItemStack item = inv.getContents()[j];
- for (int j = 0; j < 4; j++) {
- int current = j;
-
- SlimefunPlugin.runSync(() -> {
- if (current < 3) {
- p.getWorld().playSound(p.getLocation(), Sound.BLOCK_ANVIL_USE, 1F, 2F);
- } else {
- p.getWorld().playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F);
- outputInv.addItem(output);
+ if (item != null && item.getType() != Material.AIR) {
+ ItemUtils.consumeItem(item, true);
}
- }, j * 20L);
+ }
+
+ for (int j = 0; j < 4; j++) {
+ int current = j;
+
+ SlimefunPlugin.runSync(() -> {
+ if (current < 3) {
+ p.getWorld().playSound(p.getLocation(), Sound.BLOCK_ANVIL_USE, 1F, 2F);
+ } else {
+ p.getWorld().playSound(p.getLocation(), Sound.ENTITY_ARROW_HIT_PLAYER, 1F, 1F);
+ outputInv.addItem(output);
+ }
+ }, j * 20L);
+ }
+
+ } else {
+ SlimefunPlugin.getLocalization().sendMessage(p, "machines.full-inventory", true);
}
}
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 1a7615221..baf52187a 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
@@ -32,6 +32,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
*
* @see EnhancedCraftingTable
* @see MagicWorkbench
+ * @see ArmorForge
*
*/
abstract class BackpackCrafter extends MultiBlockMachine {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunBootsListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunBootsListener.java
index 8120b61c8..9d24ff2f5 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunBootsListener.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SlimefunBootsListener.java
@@ -40,7 +40,7 @@ public class SlimefunBootsListener implements Listener {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onDamage(EntityDamageEvent e) {
if (e.getEntity() instanceof Player && e.getCause() == DamageCause.FALL) {
- onFallDamage(e);
+ onFallDamage(e);
}
}
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 85092ae9a..87a2861f8 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
@@ -206,7 +206,7 @@ import me.mrCookieSlime.Slimefun.api.SlimefunItemStack;
*
*/
public final class SlimefunItemSetup {
-
+
private static boolean registeredItems = false;
private SlimefunItemSetup() {}
@@ -218,7 +218,8 @@ public final class SlimefunItemSetup {
registeredItems = true;
DefaultCategories categories = new DefaultCategories();
-
+
+ // @formatter:off (We will need to refactor this one day)
new SlimefunItem(categories.weapons, SlimefunItems.GRANDMAS_WALKING_STICK, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {null, new ItemStack(Material.OAK_LOG), null, null, new ItemStack(Material.OAK_LOG), null, null, new ItemStack(Material.OAK_LOG), null})
.register(plugin);
@@ -1092,7 +1093,7 @@ public final class SlimefunItemSetup {
new RestoredBackpack(categories.usefulItems).register(plugin);
- new SlimefunItem(categories.technicalComponents, SlimefunItems.MAGNET, RecipeType.SMELTERY,
+ new UnplaceableBlock(categories.technicalComponents, 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);
@@ -1303,15 +1304,15 @@ public final class SlimefunItemSetup {
new ItemStack[] {SlimefunItems.CARBONADO, SlimefunItems.BASIC_CIRCUIT_BOARD, SlimefunItems.CARBONADO, SlimefunItems.HEATING_COIL, SlimefunItems.REINFORCED_FURNACE, SlimefunItems.HEATING_COIL, SlimefunItems.CARBONADO, SlimefunItems.ELECTRIC_MOTOR, SlimefunItems.CARBONADO})
.register(plugin);
- new SlimefunItem(categories.technicalComponents, SlimefunItems.ELECTRO_MAGNET, RecipeType.ENHANCED_CRAFTING_TABLE,
+ new UnplaceableBlock(categories.technicalComponents, SlimefunItems.ELECTRO_MAGNET, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.NICKEL_INGOT, SlimefunItems.MAGNET, SlimefunItems.COBALT_INGOT, null, SlimefunItems.BATTERY, null, null, null, null})
.register(plugin);
- new SlimefunItem(categories.technicalComponents, SlimefunItems.ELECTRIC_MOTOR, RecipeType.ENHANCED_CRAFTING_TABLE,
+ new UnplaceableBlock(categories.technicalComponents, SlimefunItems.ELECTRIC_MOTOR, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE, null, SlimefunItems.ELECTRO_MAGNET, null, SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE})
.register(plugin);
- new SlimefunItem(categories.technicalComponents, SlimefunItems.HEATING_COIL, RecipeType.ENHANCED_CRAFTING_TABLE,
+ new UnplaceableBlock(categories.technicalComponents, SlimefunItems.HEATING_COIL, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE, SlimefunItems.ELECTRIC_MOTOR, SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE, SlimefunItems.COPPER_WIRE})
.register(plugin);
@@ -1455,7 +1456,7 @@ public final class SlimefunItemSetup {
new SlimefunItemStack(SlimefunItems.HARDENED_GLASS, 16))
.register(plugin);
- new SlimefunItem(categories.technicalComponents, SlimefunItems.COOLING_UNIT, RecipeType.ENHANCED_CRAFTING_TABLE,
+ new UnplaceableBlock(categories.technicalComponents, SlimefunItems.COOLING_UNIT, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {new ItemStack(Material.ICE), new ItemStack(Material.ICE), new ItemStack(Material.ICE), SlimefunItems.ALUMINUM_INGOT, SlimefunItems.ELECTRIC_MOTOR, SlimefunItems.ALUMINUM_INGOT, new ItemStack(Material.ICE), new ItemStack(Material.ICE), new ItemStack(Material.ICE)})
.register(plugin);
@@ -2867,7 +2868,7 @@ public final class SlimefunItemSetup {
}.register(plugin);
- new SlimefunItem(categories.cargo, SlimefunItems.CARGO_MOTOR, RecipeType.ENHANCED_CRAFTING_TABLE,
+ new UnplaceableBlock(categories.cargo, SlimefunItems.CARGO_MOTOR, RecipeType.ENHANCED_CRAFTING_TABLE,
new ItemStack[] {SlimefunItems.HARDENED_GLASS, SlimefunItems.ELECTRO_MAGNET, SlimefunItems.HARDENED_GLASS, SlimefunItems.SILVER_INGOT, SlimefunItems.ELECTRIC_MOTOR, SlimefunItems.SILVER_INGOT, SlimefunItems.HARDENED_GLASS, SlimefunItems.ELECTRO_MAGNET, SlimefunItems.HARDENED_GLASS},
new SlimefunItemStack(SlimefunItems.CARGO_MOTOR, 4))
.register(plugin);
@@ -3048,6 +3049,8 @@ public final class SlimefunItemSetup {
new ElytraCap(categories.magicalArmor, SlimefunItems.ELYTRA_CAP, RecipeType.ARMOR_FORGE,
new ItemStack[]{new ItemStack(Material.SLIME_BALL), new ItemStack(Material.SLIME_BALL), new ItemStack(Material.SLIME_BALL), SlimefunItems.ELYTRA_SCALE, SlimefunItems.ELYTRA_SCALE, SlimefunItems.ELYTRA_SCALE, new ItemStack(Material.SLIME_BALL), new ItemStack(Material.LEATHER_HELMET), new ItemStack(Material.SLIME_BALL)})
.register(plugin);
+
+ // @formatter:on
}
private static void registerArmorSet(Category category, ItemStack baseComponent, ItemStack[] items, String idSyntax, boolean vanilla, PotionEffect[][] effects, SlimefunAddon addon) {
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChestMenuUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChestMenuUtils.java
index 7f566db35..0cb2ea26f 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChestMenuUtils.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ChestMenuUtils.java
@@ -29,6 +29,9 @@ public final class ChestMenuUtils {
private static final ItemStack INPUT_SLOT = new SlimefunItemStack("_UI_INPUT_SLOT", Material.CYAN_STAINED_GLASS_PANE, " ");
private static final ItemStack OUTPUT_SLOT = new SlimefunItemStack("_UI_OUTPUT_SLOT", Material.ORANGE_STAINED_GLASS_PANE, " ");
+ private static final ItemStack NO_PERMISSION = new SlimefunItemStack("_UI_NO_PERMISSION", Material.BARRIER, "No Permission");
+ private static final ItemStack NOT_RESEARCHED = new SlimefunItemStack("_UI_NOT_RESEARCHED", Material.BARRIER, "Not researched");
+
private static final ItemStack BACK_BUTTON = new SlimefunItemStack("_UI_BACK", Material.ENCHANTED_BOOK, "&7\u21E6 Back", meta -> meta.addItemFlags(ItemFlag.HIDE_ENCHANTS));
private static final ItemStack MENU_BUTTON = new SlimefunItemStack("_UI_MENU", Material.COMPARATOR, "&eSettings / Info", "", "&7\u21E8 Click to see more");
private static final ItemStack SEARCH_BUTTON = new SlimefunItemStack("_UI_SEARCH", Material.NAME_TAG, "&bSearch");
@@ -46,6 +49,16 @@ public final class ChestMenuUtils {
return UI_BACKGROUND;
}
+ @Nonnull
+ public static ItemStack getNoPermissionItem() {
+ return NO_PERMISSION;
+ }
+
+ @Nonnull
+ public static ItemStack getNotResearchedItem() {
+ return NOT_RESEARCHED;
+ }
+
@Nonnull
public static ItemStack getInputSlotTexture() {
return INPUT_SLOT;
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterials.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterials.java
index 274f0ed09..7b4f8de31 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterials.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/ColoredMaterials.java
@@ -4,6 +4,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import javax.annotation.Nonnull;
+
+import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
@@ -24,11 +27,13 @@ public final class ColoredMaterials {
* constructor to be private.
*/
private ColoredMaterials() {}
+
+ // @formatter:off (We want this to stay formatted like this)
/**
* This {@link List} contains all wool colors ordered by their appearance ingame.
*/
- public static final List WOOL = Collections.unmodifiableList(Arrays.asList(
+ public static final List WOOL = asList(new Material[] {
Material.WHITE_WOOL,
Material.ORANGE_WOOL,
Material.MAGENTA_WOOL,
@@ -45,12 +50,12 @@ public final class ColoredMaterials {
Material.GREEN_WOOL,
Material.RED_WOOL,
Material.BLACK_WOOL
- ));
+ });
/**
* This {@link List} contains all stained glass colors ordered by their appearance ingame.
*/
- public static final List STAINED_GLASS = Collections.unmodifiableList(Arrays.asList(
+ public static final List STAINED_GLASS = asList(new Material[] {
Material.WHITE_STAINED_GLASS,
Material.ORANGE_STAINED_GLASS,
Material.MAGENTA_STAINED_GLASS,
@@ -67,12 +72,12 @@ public final class ColoredMaterials {
Material.GREEN_STAINED_GLASS,
Material.RED_STAINED_GLASS,
Material.BLACK_STAINED_GLASS
- ));
+ });
/**
* This {@link List} contains all stained glass pane colors ordered by their appearance ingame.
*/
- public static final List STAINED_GLASS_PANE = Collections.unmodifiableList(Arrays.asList(
+ public static final List STAINED_GLASS_PANE = asList(new Material[] {
Material.WHITE_STAINED_GLASS_PANE,
Material.ORANGE_STAINED_GLASS_PANE,
Material.MAGENTA_STAINED_GLASS_PANE,
@@ -89,12 +94,12 @@ public final class ColoredMaterials {
Material.GREEN_STAINED_GLASS_PANE,
Material.RED_STAINED_GLASS_PANE,
Material.BLACK_STAINED_GLASS_PANE
- ));
+ });
/**
* This {@link List} contains all terracotta colors ordered by their appearance ingame.
*/
- public static final List TERRACOTTA = Collections.unmodifiableList(Arrays.asList(
+ public static final List TERRACOTTA = asList(new Material[] {
Material.WHITE_TERRACOTTA,
Material.ORANGE_TERRACOTTA,
Material.MAGENTA_TERRACOTTA,
@@ -111,12 +116,12 @@ public final class ColoredMaterials {
Material.GREEN_TERRACOTTA,
Material.RED_TERRACOTTA,
Material.BLACK_TERRACOTTA
- ));
+ });
/**
* This {@link List} contains all glazed terracotta colors ordered by their appearance ingame.
*/
- public static final List GLAZED_TERRACOTTA = Collections.unmodifiableList(Arrays.asList(
+ public static final List GLAZED_TERRACOTTA = asList(new Material[] {
Material.WHITE_GLAZED_TERRACOTTA,
Material.ORANGE_GLAZED_TERRACOTTA,
Material.MAGENTA_GLAZED_TERRACOTTA,
@@ -133,12 +138,12 @@ public final class ColoredMaterials {
Material.GREEN_GLAZED_TERRACOTTA,
Material.RED_GLAZED_TERRACOTTA,
Material.BLACK_GLAZED_TERRACOTTA
- ));
+ });
/**
* This {@link List} contains all concrete colors ordered by their appearance ingame.
*/
- public static final List CONCRETE = Collections.unmodifiableList(Arrays.asList(
+ public static final List CONCRETE = asList(new Material[] {
Material.WHITE_CONCRETE,
Material.ORANGE_CONCRETE,
Material.MAGENTA_CONCRETE,
@@ -155,6 +160,16 @@ public final class ColoredMaterials {
Material.GREEN_CONCRETE,
Material.RED_CONCRETE,
Material.BLACK_CONCRETE
- ));
+ });
+
+ // @formatter:on
+
+ @Nonnull
+ private static List asList(@Nonnull Material[] materials) {
+ Validate.noNullElements(materials, "The List cannot contain any null elements");
+ Validate.isTrue(materials.length == 16, "Expected 16, received: " + materials.length + ". Did you miss a color?");
+
+ return Collections.unmodifiableList(Arrays.asList(materials));
+ }
}
diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java
index 8dd69f881..d752b5ec8 100644
--- a/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java
+++ b/src/main/java/io/github/thebusybiscuit/slimefun4/utils/NumberUtils.java
@@ -1,6 +1,7 @@
package io.github.thebusybiscuit.slimefun4.utils;
import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.time.Duration;
import java.time.LocalDateTime;
@@ -24,7 +25,7 @@ public final class NumberUtils {
/**
* This is our {@link DecimalFormat} for decimal values.
*/
- private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##");
+ private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##", DecimalFormatSymbols.getInstance(Locale.ROOT));
/**
* We do not want any instance of this to be created.
@@ -105,8 +106,31 @@ public final class NumberUtils {
*/
@Nonnull
public static String getElapsedTime(@Nonnull LocalDateTime date) {
- Validate.notNull(date, "Provided date was null");
- long hours = Duration.between(date, LocalDateTime.now()).toHours();
+ return getElapsedTime(LocalDateTime.now(), date);
+ }
+
+ /**
+ * This returns the elapsed time between the two given {@link LocalDateTime LocalDateTimes}.
+ * The output will be nicely formatted based on the elapsed hours or days between the
+ * given {@link LocalDateTime LocalDateTime}.
+ *
+ * If a {@link LocalDateTime} from today and yesterday (exactly 24h apart) was passed it
+ * will return {@code "1d"}.
+ * One hour later it will read {@code "1d 1h"}. For values smaller than an hour {@code "< 1h"}
+ * will be returned instead.
+ *
+ * @param start
+ * The starting {@link LocalDateTime}.
+ * @param end
+ * The ending {@link LocalDateTime}.
+ *
+ * @return The elapsed time as a {@link String}
+ */
+ @Nonnull
+ public static String getElapsedTime(@Nonnull LocalDateTime start, @Nonnull LocalDateTime end) {
+ Validate.notNull(start, "Provided start was null");
+ Validate.notNull(end, "Provided end was null");
+ long hours = Duration.between(start, end).toHours();
if (hours == 0) {
return "< 1h";
@@ -133,12 +157,24 @@ public final class NumberUtils {
return timeleft + seconds + "s";
}
+ /**
+ * This method parses a {@link String} into an {@link Integer}.
+ * If the {@link String} could not be parsed correctly, the provided
+ * default value will be returned instead.
+ *
+ * @param str
+ * The {@link String} to parse
+ * @param defaultValue
+ * The default value for when the {@link String} could not be parsed
+ *
+ * @return The resulting {@link Integer}
+ */
public static int getInt(@Nonnull String str, int defaultValue) {
if (PatternUtils.NUMERIC.matcher(str).matches()) {
return Integer.parseInt(str);
+ } else {
+ return defaultValue;
}
-
- return defaultValue;
}
@Nonnull
@@ -183,6 +219,8 @@ public final class NumberUtils {
* The value to clamp
* @param max
* The maximum value
+ *
+ * @return The clamped value
*/
public static int clamp(int min, int value, int max) {
if (value < min) {
diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java
index daf180de3..b6e36dcd8 100644
--- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java
+++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java
@@ -5,9 +5,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
+import org.apache.commons.lang.Validate;
import org.bukkit.Bukkit;
+import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
@@ -238,31 +241,30 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock,
BlockMenu inv = BlockStorage.getInventory(b);
if (isProcessing(b)) {
- int timeleft = progress.get(b);
- if (timeleft > 0) {
- ChestMenuUtils.updateProgressbar(inv, 22, timeleft, processing.get(b).getTicks(), getProgressBar());
+ if (takeCharge(b.getLocation())) {
- if (isChargeable()) {
- if (getCharge(b.getLocation()) < getEnergyConsumption()) {
- return;
+ int timeleft = progress.get(b);
+
+ if (timeleft > 0) {
+ ChestMenuUtils.updateProgressbar(inv, 22, timeleft, processing.get(b).getTicks(), getProgressBar());
+
+ progress.put(b, timeleft - 1);
+ } else {
+
+ inv.replaceExistingItem(22, new CustomItem(Material.BLACK_STAINED_GLASS_PANE, " "));
+
+ for (ItemStack output : processing.get(b).getOutput()) {
+ inv.pushItem(output.clone(), getOutputSlots());
}
- removeCharge(b.getLocation(), getEnergyConsumption());
+ Bukkit.getPluginManager().callEvent(new AsyncMachineProcessCompleteEvent(b.getLocation(), AContainer.this, getProcessing(b)));
+
+ progress.remove(b);
+ processing.remove(b);
}
- progress.put(b, timeleft - 1);
- } else {
- inv.replaceExistingItem(22, new CustomItem(Material.BLACK_STAINED_GLASS_PANE, " "));
-
- for (ItemStack output : processing.get(b).getOutput()) {
- inv.pushItem(output.clone(), getOutputSlots());
- }
-
- Bukkit.getPluginManager().callEvent(new AsyncMachineProcessCompleteEvent(b.getLocation(), AContainer.this, getProcessing(b)));
-
- progress.remove(b);
- processing.remove(b);
}
+
} else {
MachineRecipe next = findNextRecipe(inv);
@@ -273,6 +275,26 @@ public abstract class AContainer extends SlimefunItem implements InventoryBlock,
}
}
+ /**
+ * This method will remove charge from a location if it is chargeable.
+ *
+ * @param l
+ * location to try to remove charge from
+ * @return Whether charge was taken if its chargeable
+ */
+ protected boolean takeCharge(@Nonnull Location l) {
+ Validate.notNull(l, "Can't attempt to take charge from a null location!");
+
+ if (isChargeable()) {
+ if (getCharge(l) < getEnergyConsumption()) {
+ return false;
+ }
+
+ removeCharge(l, getEnergyConsumption());
+ }
+ return true;
+ }
+
protected MachineRecipe findNextRecipe(BlockMenu inv) {
Map inventory = new HashMap<>();
diff --git a/src/main/resources/languages/messages_hu.yml b/src/main/resources/languages/messages_hu.yml
index 8320c757b..5bfa56bdd 100644
--- a/src/main/resources/languages/messages_hu.yml
+++ b/src/main/resources/languages/messages_hu.yml
@@ -244,6 +244,7 @@ machines:
finished: Az Industrial Miner-ed kész! Összesen %ores% ércet szerzett!
anvil:
not-working: "&4Nem használhatsz Slimefun tárgyakat az üllőben!"
+ mcmmo-salvaging: "&4Nem hasznosíthatsz újra Slimefun tárgyakat!"
backpack:
already-open: "&cSajnáljuk, ez a hátizsák valahol máshol már nyitva van!"
no-stack: "&cNem halmozhatsz hátizsákokat"
@@ -306,7 +307,7 @@ android:
rating:
own: "&4Nem értékelheted a saját szkriptedet!"
already: "&4Ezt a szkriptet már értékelted!"
- editor: Szkript Szerkesztő
+ editor: Szkript szerkesztő
languages:
default: Szerver-alapértelmezett
en: Angol
@@ -328,7 +329,6 @@ languages:
zh-CN: Kínai (Kína)
el: Görög
he: Héber
- pt-BR: Portugál (Brazília)
ar: Arab
af: Afrikaans
da: Dán
@@ -341,6 +341,7 @@ languages:
th: Thai
ro: Román
pt: Portugál (Portugália)
+ pt-BR: Portugál (Brazília)
bg: Bolgár
ko: Koreai
tr: Török
@@ -355,5 +356,7 @@ villagers:
no-trading: "&4Nem cserélhetsz Slimefun tárgyakat falusiakkal!"
cartography_table:
not-working: "&4Nem használhatsz Slimefun tárgyakat térképasztalban."
+cauldron:
+ no-discoloring: "&4Nem színteleníthetsz Slimefun páncélt!"
miner:
no-ores: "&eSajnálom, nem találtam semmilyen ércet a közelben!"
diff --git a/src/main/resources/languages/researches_hu.yml b/src/main/resources/languages/researches_hu.yml
index 2715e2184..f1bf8cedc 100644
--- a/src/main/resources/languages/researches_hu.yml
+++ b/src/main/resources/languages/researches_hu.yml
@@ -246,3 +246,4 @@ slimefun:
caveman_talisman: Az Ősember talizmánja
even_higher_tier_capacitors: 3. szintű kondenzátorok
elytra_cap: Ütközésvédelem
+ energy_connectors: Vezetékes csatlakozás
diff --git a/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestNumberUtils.java b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestNumberUtils.java
new file mode 100644
index 000000000..6b33de558
--- /dev/null
+++ b/src/test/java/io/github/thebusybiscuit/slimefun4/testing/tests/utils/TestNumberUtils.java
@@ -0,0 +1,78 @@
+package io.github.thebusybiscuit.slimefun4.testing.tests.utils;
+
+import java.time.LocalDateTime;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
+
+class TestNumberUtils {
+
+ @Test
+ @DisplayName("Test NumberUtils.clamp(...)")
+ void testHumanize() {
+ // Below minimum
+ Assertions.assertEquals(2, NumberUtils.clamp(2, 0, 5));
+
+ // Normal
+ Assertions.assertEquals(3, NumberUtils.clamp(0, 3, 5));
+
+ // Above maximum
+ Assertions.assertEquals(20, NumberUtils.clamp(1, 100, 20));
+ }
+
+ @Test
+ @DisplayName("Test elapsed time string")
+ void testElapsedTime() {
+ LocalDateTime start = LocalDateTime.now();
+
+ LocalDateTime a = start.plusDays(1);
+ Assertions.assertEquals("1d", NumberUtils.getElapsedTime(start, a));
+
+ LocalDateTime b = start.plusHours(25);
+ Assertions.assertEquals("1d 1h", NumberUtils.getElapsedTime(start, b));
+
+ LocalDateTime c = start.plusHours(1);
+ Assertions.assertEquals("1h", NumberUtils.getElapsedTime(start, c));
+
+ LocalDateTime d = start.plusMinutes(12);
+ Assertions.assertEquals("< 1h", NumberUtils.getElapsedTime(start, d));
+ }
+
+ @Test
+ @DisplayName("Test Integer parsing")
+ void testIntegerParsing() {
+ Assertions.assertEquals(6, NumberUtils.getInt("6", 0));
+ Assertions.assertEquals(12, NumberUtils.getInt("I am a String", 12));
+ }
+
+ @Test
+ @DisplayName("Test nullable Long")
+ void testNullableLong() {
+ Assertions.assertEquals(10, NumberUtils.getLong(10L, 20L));
+ Assertions.assertEquals(20, NumberUtils.getLong(null, 20L));
+ }
+
+ @Test
+ @DisplayName("Test nullable Int")
+ void testNullableInt() {
+ Assertions.assertEquals(10, NumberUtils.getInt(10, 20));
+ Assertions.assertEquals(20, NumberUtils.getInt((Integer) null, 20));
+ }
+
+ @Test
+ @DisplayName("Test nullable Float")
+ void testNullableFloat() {
+ Assertions.assertEquals(10, NumberUtils.getFloat(10F, 20F));
+ Assertions.assertEquals(20, NumberUtils.getFloat(null, 20F));
+ }
+
+ @Test
+ @DisplayName("Test decimal rounding")
+ void testRounding() {
+ Assertions.assertEquals("5.25", NumberUtils.roundDecimalNumber(5.249999999999));
+ }
+
+}