diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 147c3339f..61ba80048 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -36,6 +36,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=BentoBoxWorld_BentoBox
+ - name: Debug - List target directory
+ run: ls -la /home/runner/work/BentoBox/BentoBox/target
- run: mvn --batch-mode clean org.jacoco:jacoco-maven-plugin:prepare-agent install
- run: mkdir staging && cp target/*.jar staging
- name: Save artifacts
diff --git a/.github/workflows/modrinth-publish.yml b/.github/workflows/modrinth-publish.yml
index a6602a034..e0d2a0a91 100644
--- a/.github/workflows/modrinth-publish.yml
+++ b/.github/workflows/modrinth-publish.yml
@@ -25,7 +25,8 @@ jobs:
- name: Build and package with Maven
run: mvn -B clean package -DskipTests -Pmaster --file pom.xml
-
+ - name: Debug - List target directory
+ run: ls -la /home/runner/work/BentoBox/BentoBox/target
- name: Upload to Modrinth
uses: cloudnode-pro/modrinth-publish@v2
with:
diff --git a/pom.xml b/pom.xml
index 632cdfe5e..aa5360ce1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,7 +74,7 @@
-LOCAL
- 3.3.4
+ 3.3.5
bentobox-world
https://sonarcloud.io
${project.basedir}/lib
@@ -161,6 +161,17 @@
MG-Dev Jenkins CI Maven Repository
https://ci.mg-dev.eu/plugin/repository/everything
+
+
+ multiverse-multiverse-releases
+ Multiverse Repository
+ https://repo.onarandombox.com/multiverse-releases
+
+
+ multiverse-multiverse-snapshots
+ Multiverse Repository
+ https://repo.onarandombox.com/multiverse-snapshots
+
nexus
@@ -304,11 +315,29 @@
5.3.5
provided
+
+ org.mvplugins.multiverse.core
+ multiverse-core
+ 5.0.0-SNAPSHOT
+ provided
+
+
+ com.onarandombox.multiversecore
+ multiverse-core
+ 4.3.16
+ provided
+
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.3.0
com.github.Marcono1234
@@ -512,10 +541,10 @@
org.bstats
world.bentobox.bentobox.util.metrics
-
+
io.papermc.lib
world.bentobox.bentobox.paperlib
diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java
index 9dc924a89..36495f346 100644
--- a/src/main/java/world/bentobox/bentobox/BentoBox.java
+++ b/src/main/java/world/bentobox/bentobox/BentoBox.java
@@ -27,7 +27,8 @@
import world.bentobox.bentobox.hooks.FancyNpcsHook;
import world.bentobox.bentobox.hooks.ItemsAdderHook;
import world.bentobox.bentobox.hooks.MultipaperHook;
-import world.bentobox.bentobox.hooks.MultiverseCoreHook;
+import world.bentobox.bentobox.hooks.MultiverseCore4Hook;
+import world.bentobox.bentobox.hooks.MultiverseCore5Hook;
import world.bentobox.bentobox.hooks.MyWorldsHook;
import world.bentobox.bentobox.hooks.MythicMobsHook;
import world.bentobox.bentobox.hooks.SlimefunHook;
@@ -233,7 +234,11 @@ private void completeSetup(long loadTime) {
// Register Multiverse hook - MV loads AFTER BentoBox
// Make sure all worlds are already registered to Multiverse.
- hooksManager.registerHook(new MultiverseCoreHook());
+ if (hasClass("org.mvplugins.multiverse.core.MultiverseCore")) {
+ hooksManager.registerHook(new MultiverseCore5Hook());
+ } else if (hasClass("com.onarandombox.MultiverseCore.MultiverseCore")) {
+ hooksManager.registerHook(new MultiverseCore4Hook());
+ }
hooksManager.registerHook(new MyWorldsHook());
islandWorldManager.registerWorldsToMultiverse(true);
@@ -281,6 +286,15 @@ private void completeSetup(long loadTime) {
}
}
+ private boolean hasClass(String className) {
+ try {
+ Class.forName(className);
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+
private void fireCriticalError(String message, String error) {
logError("*****************CRITICAL ERROR!******************");
logError(message);
diff --git a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeDisplayCommand.java b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeDisplayCommand.java
index dbdf75342..e1b9dab1f 100644
--- a/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeDisplayCommand.java
+++ b/src/main/java/world/bentobox/bentobox/api/commands/admin/range/AdminRangeDisplayCommand.java
@@ -13,7 +13,6 @@
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
-import world.bentobox.bentobox.util.Util;
/**
* @author Poslovitch
@@ -24,9 +23,8 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
private static final String DISPLAY = "display";
private static final String SHOW = "show";
private static final String HIDE = "hide";
- public static final Particle PARTICLE = Util.findFirstMatchingEnum(Particle.class, "REDSTONE", "DUST");
- private static final Particle PARTICLE2 = Util.findFirstMatchingEnum(Particle.class, "VILLAGER_HAPPY",
- "HAPPY_VILLAGER");
+ public static final Particle PARTICLE = Particle.DUST;
+ private static final Particle PARTICLE2 = Particle.DUST;
// Map of users to which ranges must be displayed
private final Map displayRanges = new HashMap<>();
@@ -80,7 +78,8 @@ private void showZones(User user) {
// Draw the default protected area if island protected zone is different
if (island.getProtectionRange() != getPlugin().getIWM().getIslandProtectionRange(getWorld())) {
- drawZone(user, PARTICLE2, null, island, getPlugin().getIWM().getIslandProtectionRange(getWorld()));
+ drawZone(user, PARTICLE2, new Particle.DustOptions(Color.GREEN, 1.0F), island,
+ getPlugin().getIWM().getIslandProtectionRange(getWorld()));
}
// Draw the island area
diff --git a/src/main/java/world/bentobox/bentobox/api/github/GitHubWebAPI.java b/src/main/java/world/bentobox/bentobox/api/github/GitHubWebAPI.java
new file mode 100644
index 000000000..ae900abfc
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/api/github/GitHubWebAPI.java
@@ -0,0 +1,87 @@
+package world.bentobox.bentobox.api.github;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Scanner;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+import world.bentobox.bentobox.api.github.objects.repositories.GitHubRepository;
+
+/**
+ * Handles interactions with the GitHub API.
+ * This is a rewrite of {@link https://github.com/Poslovitch/GitHubWebAPI4}, which is now out of date
+ * and this code only implements parts that are used by BentoBox and not all of it.
+ */
+public class GitHubWebAPI {
+
+ private static final String API_BASE_URL = "https://api.github.com/";
+ private static final long RATE_LIMIT_INTERVAL_MS = 1000; // 1 second
+ private static long lastRequestTime = 0;
+
+ private final ExecutorService executor = Executors.newCachedThreadPool();
+
+ /**
+ * Fetches the content of a given API endpoint.
+ *
+ * @param endpoint The API endpoint to fetch.
+ * @return The JSON response as a JsonObject.
+ * @throws IOException If an error occurs during the request.
+ */
+ public synchronized JsonObject fetch(String endpoint) throws IOException {
+ long currentTime = System.currentTimeMillis();
+ long timeSinceLastRequest = currentTime - lastRequestTime;
+
+ if (timeSinceLastRequest < RATE_LIMIT_INTERVAL_MS) {
+ try {
+ Thread.sleep(RATE_LIMIT_INTERVAL_MS - timeSinceLastRequest);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException("Thread interrupted while waiting for rate limit", e);
+ }
+ }
+
+ lastRequestTime = System.currentTimeMillis();
+
+ URL url = new URL(API_BASE_URL + endpoint);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ connection.setRequestProperty("Accept", "application/vnd.github.v3+json");
+ connection.setRequestProperty("User-Agent", "BentoBox"); // Add User-Agent header
+
+ int responseCode = connection.getResponseCode();
+ if (responseCode == 403) {
+ throw new IOException("GitHub API rate limit exceeded or access forbidden. Response code: " + responseCode);
+ }
+
+ try (Scanner scanner = new Scanner(connection.getInputStream())) {
+ String response = scanner.useDelimiter("\\A").next();
+ return JsonParser.parseString(response).getAsJsonObject();
+ }
+ }
+
+ /**
+ * Fetches the content of a given API endpoint asynchronously.
+ *
+ * @param endpoint The API endpoint to fetch.
+ * @return A CompletableFuture containing the JSON response as a JsonObject.
+ */
+ public CompletableFuture fetchAsync(String endpoint) {
+ return CompletableFuture.supplyAsync(() -> {
+ try {
+ return fetch(endpoint);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to fetch data from GitHub API", e);
+ }
+ }, executor);
+ }
+
+ public GitHubRepository getRepository(String username, String repo) {
+ return new GitHubRepository(this, username + "/" + repo);
+ }
+}
diff --git a/src/main/java/world/bentobox/bentobox/api/github/objects/repositories/GitHubContributor.java b/src/main/java/world/bentobox/bentobox/api/github/objects/repositories/GitHubContributor.java
new file mode 100644
index 000000000..a468aa6de
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/api/github/objects/repositories/GitHubContributor.java
@@ -0,0 +1,23 @@
+package world.bentobox.bentobox.api.github.objects.repositories;
+
+/**
+ * Represents a contributor to a GitHub repository.
+ */
+public class GitHubContributor {
+
+ private final String username;
+ private final int contributionsAmount;
+
+ public GitHubContributor(String username, int contributionsAmount) {
+ this.username = username;
+ this.contributionsAmount = contributionsAmount;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public int getContributionsAmount() {
+ return contributionsAmount;
+ }
+}
diff --git a/src/main/java/world/bentobox/bentobox/api/github/objects/repositories/GitHubFile.java b/src/main/java/world/bentobox/bentobox/api/github/objects/repositories/GitHubFile.java
new file mode 100644
index 000000000..fedea7a1a
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/api/github/objects/repositories/GitHubFile.java
@@ -0,0 +1,37 @@
+package world.bentobox.bentobox.api.github.objects.repositories;
+
+import java.io.IOException;
+
+import com.google.gson.JsonObject;
+
+import world.bentobox.bentobox.api.github.GitHubWebAPI;
+
+/**
+ * Represents a file or a directory.
+ */
+public class GitHubFile {
+
+ private final GitHubWebAPI api;
+ private final String path;
+
+ public GitHubFile(GitHubWebAPI api, GitHubRepository gitHubRepository, String fullpath) {
+ this.api = api;
+ this.path = "repos/" + gitHubRepository.getFullName() + fullpath;
+ }
+
+ /**
+ * Returns the content of this file.
+ * @return the content of this file in Base64 or nothing if the connection to the GitHub API could not be established.
+ * @throws IOException - If an error occurs during the request.
+ */
+ public String getContent() throws IllegalAccessException {
+ JsonObject response;
+ try {
+ response = api.fetch(path);
+ } catch (IOException e) {
+ // Cannot get a connection for some reason
+ return "";
+ }
+ return response.get("content").getAsString();
+ }
+}
diff --git a/src/main/java/world/bentobox/bentobox/api/github/objects/repositories/GitHubRepository.java b/src/main/java/world/bentobox/bentobox/api/github/objects/repositories/GitHubRepository.java
new file mode 100644
index 000000000..ea5e8f2fa
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/api/github/objects/repositories/GitHubRepository.java
@@ -0,0 +1,57 @@
+package world.bentobox.bentobox.api.github.objects.repositories;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+import world.bentobox.bentobox.api.github.GitHubWebAPI;
+
+/**
+ * Represents a GitHub repository and provides methods to fetch its data.
+ */
+public class GitHubRepository {
+
+ private final GitHubWebAPI api;
+ private final String fullName;
+
+ public GitHubRepository(GitHubWebAPI api, String fullName) {
+ this.api = api;
+ this.fullName = fullName;
+ }
+
+ public String getFullName() {
+ return fullName;
+ }
+
+ /**
+ * Fetches the content of a file in the repository.
+ *
+ * @param fpath The path to the file.
+ * @return A GitHubFile object representing the file content.
+ * @throws Exception If an error occurs during the request.
+ */
+ public GitHubFile getContent(String path) throws Exception {
+ return new GitHubFile(api, this, "/contents/" + path);
+ }
+
+ /**
+ * Fetches the list of contributors to the repository.
+ *
+ * @return A list of GitHubContributor objects.
+ * @throws Exception If an error occurs during the request.
+ */
+ public List getContributors() throws Exception {
+ JsonArray response = api.fetch("repos/" + fullName + "/contributors").getAsJsonArray();
+ List contributors = new ArrayList<>();
+ response.forEach(element -> {
+ JsonObject contributor = element.getAsJsonObject();
+ contributors.add(new GitHubContributor(
+ contributor.get("login").getAsString(),
+ contributor.get("contributions").getAsInt()
+ ));
+ });
+ return contributors;
+ }
+}
diff --git a/src/main/java/world/bentobox/bentobox/api/user/User.java b/src/main/java/world/bentobox/bentobox/api/user/User.java
index a5bef5a3f..65bc2fad8 100644
--- a/src/main/java/world/bentobox/bentobox/api/user/User.java
+++ b/src/main/java/world/bentobox/bentobox/api/user/User.java
@@ -12,6 +12,7 @@
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import org.apache.commons.lang.math.NumberUtils;
import org.bukkit.Bukkit;
@@ -823,8 +824,10 @@ public boolean inWorld() {
*/
public void spawnParticle(Particle particle, @Nullable Object dustOptions, double x, double y, double z) {
Class> expectedClass = VALIDATION_CHECK.get(particle);
- if (expectedClass == null)
- throw new IllegalArgumentException("Unexpected value: " + particle);
+ if (expectedClass == null) {
+ throw new IllegalArgumentException("Unexpected value: " + particle + "\nExpected one of:"
+ + VALIDATION_CHECK.keySet().stream().map(Particle::name).collect(Collectors.joining(", ")));
+ }
if (!(expectedClass.isInstance(dustOptions))) {
throw new IllegalArgumentException("A non-null " + expectedClass.getSimpleName()
diff --git a/src/main/java/world/bentobox/bentobox/hooks/MultiverseCore4Hook.java b/src/main/java/world/bentobox/bentobox/hooks/MultiverseCore4Hook.java
new file mode 100644
index 000000000..2959331ab
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/hooks/MultiverseCore4Hook.java
@@ -0,0 +1,64 @@
+package world.bentobox.bentobox.hooks;
+
+import com.onarandombox.MultiverseCore.MultiverseCore;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.World;
+import world.bentobox.bentobox.BentoBox;
+import world.bentobox.bentobox.api.hooks.Hook;
+
+public class MultiverseCore4Hook extends Hook implements WorldManagementHook {
+
+ public MultiverseCore4Hook() {
+ super("Multiverse-Core", Material.COMPASS);
+ }
+
+ /**
+ * Register the world with Multiverse
+ * @param world - world to register
+ * @param islandWorld - if true, then this is an island world
+ */
+ @Override
+ public void registerWorld(World world, boolean islandWorld) {
+ MultiverseCore core = (MultiverseCore) Bukkit.getPluginManager().getPlugin("Multiverse-Core");
+ if (core == null) {
+ return;
+ }
+ String generator = islandWorld ? getGenerator(world) : null;
+ core.getMVWorldManager().addWorld(
+ world.getName(),
+ world.getEnvironment(),
+ String.valueOf(world.getSeed()),
+ world.getWorldType(),
+ world.canGenerateStructures(),
+ generator
+ );
+ core.getMVWorldManager().getMVWorld(world.getName()).setAutoLoad(false);
+ }
+
+ private String getGenerator(World world) {
+ return BentoBox.getInstance().getIWM().getAddon(world)
+ .map(gm -> gm.getDefaultWorldGenerator(world.getName(), "") != null).orElse(false)
+ ? BentoBox.getInstance().getName()
+ : null;
+ }
+
+ @Override
+ public void unregisterWorld(World world) {
+ MultiverseCore core = (MultiverseCore) Bukkit.getPluginManager().getPlugin("Multiverse-Core");
+ if (core == null) {
+ return;
+ }
+ core.getMVWorldManager().removeWorldFromConfig(world.getName());
+ }
+
+ @Override
+ public boolean hook() {
+ return true; // The hook process shouldn't fail
+ }
+
+ @Override
+ public String getFailureCause() {
+ return null; // The hook process shouldn't fail
+ }
+}
diff --git a/src/main/java/world/bentobox/bentobox/hooks/MultiverseCore5Hook.java b/src/main/java/world/bentobox/bentobox/hooks/MultiverseCore5Hook.java
new file mode 100644
index 000000000..3d96213d4
--- /dev/null
+++ b/src/main/java/world/bentobox/bentobox/hooks/MultiverseCore5Hook.java
@@ -0,0 +1,59 @@
+package world.bentobox.bentobox.hooks;
+
+import org.bukkit.Material;
+import org.bukkit.World;
+
+import org.mvplugins.multiverse.core.MultiverseCoreApi;
+import org.mvplugins.multiverse.core.world.options.ImportWorldOptions;
+import world.bentobox.bentobox.BentoBox;
+import world.bentobox.bentobox.api.hooks.Hook;
+
+/**
+ * Provides implementation and interfacing to interact with Multiverse.
+ *
+ * @author Poslovitch
+ */
+public class MultiverseCore5Hook extends Hook implements WorldManagementHook {
+
+ public MultiverseCore5Hook() {
+ super("Multiverse-Core", Material.COMPASS);
+ }
+
+ /**
+ * Register the world with Multiverse
+ * @param world - world to register
+ * @param islandWorld - if true, then this is an island world
+ */
+ @Override
+ public void registerWorld(World world, boolean islandWorld) {
+ MultiverseCoreApi api = MultiverseCoreApi.get();
+ String generator = islandWorld ? getGenerator(world) : null;
+ api.getWorldManager().importWorld(ImportWorldOptions.worldName(world.getName())
+ .environment(world.getEnvironment())
+ .generator(generator))
+ .peek(mvWorld -> mvWorld.setAutoLoad(false)); // Let BentoBox handle loading on startup
+ }
+
+ private String getGenerator(World world) {
+ return BentoBox.getInstance().getIWM().getAddon(world)
+ .map(gm -> gm.getDefaultWorldGenerator(world.getName(), "") != null).orElse(false)
+ ? BentoBox.getInstance().getName()
+ : null;
+ }
+
+ @Override
+ public void unregisterWorld(World world) {
+ MultiverseCoreApi api = MultiverseCoreApi.get();
+ api.getWorldManager().getWorld(world.getName()).peek(api.getWorldManager()::removeWorld);
+ }
+
+ @Override
+ public boolean hook() {
+ return true; // The hook process shouldn't fail
+ }
+
+ @Override
+ public String getFailureCause() {
+ return null; // The hook process shouldn't fail
+ }
+}
diff --git a/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java b/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java
deleted file mode 100644
index 56e575e77..000000000
--- a/src/main/java/world/bentobox/bentobox/hooks/MultiverseCoreHook.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package world.bentobox.bentobox.hooks;
-
-import java.util.Locale;
-
-import org.bukkit.Bukkit;
-import org.bukkit.Material;
-import org.bukkit.World;
-
-import world.bentobox.bentobox.BentoBox;
-import world.bentobox.bentobox.api.hooks.Hook;
-
-/**
- * Provides implementation and interfacing to interact with Multiverse.
- *
- * @author Poslovitch
- */
-public class MultiverseCoreHook extends Hook implements WorldManagementHook {
-
- private static final String MULTIVERSE_SET_GENERATOR = "mv modify set generator ";
- private static final String MULTIVERSE_IMPORT = "mv import ";
-
- public MultiverseCoreHook() {
- super("Multiverse-Core", Material.COMPASS);
- }
-
- /**
- * Register the world with Multiverse
- * @param world - world to register
- * @param islandWorld - if true, then this is an island world
- */
- @Override
- public void registerWorld(World world, boolean islandWorld) {
- if (islandWorld) {
- // Only register generator if one is defined in the addon (is not null)
- String generator = BentoBox.getInstance().getIWM().getAddon(world)
- .map(gm -> gm.getDefaultWorldGenerator(world.getName(), "") != null).orElse(false)
- ? " -g " + BentoBox.getInstance().getName()
- : "";
- String cmd1 = MULTIVERSE_IMPORT + world.getName() + " "
- + world.getEnvironment().name().toLowerCase(Locale.ENGLISH) + generator;
- String cmd2 = MULTIVERSE_SET_GENERATOR + BentoBox.getInstance().getName() + " " + world.getName();
- Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd1);
- if (!generator.isEmpty()) {
- // Register the generator
- Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd2);
- }
- } else {
- // Set the generator to null - this will remove any previous registration
- String cmd1 = MULTIVERSE_IMPORT + world.getName() + " "
- + world.getEnvironment().name().toLowerCase(Locale.ENGLISH);
- String cmd2 = MULTIVERSE_SET_GENERATOR + "null " + world.getName();
- Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd1);
- Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd2);
- }
- }
-
- @Override
- public void unregisterWorld(World world) {
- String cmd = "mv remove " + world.getName();
- Bukkit.getServer().dispatchCommand(Bukkit.getServer().getConsoleSender(), cmd);
- }
-
- @Override
- public boolean hook() {
- return true; // The hook process shouldn't fail
- }
-
- @Override
- public String getFailureCause() {
- return null; // The hook process shouldn't fail
- }
-}
diff --git a/src/main/java/world/bentobox/bentobox/managers/WebManager.java b/src/main/java/world/bentobox/bentobox/managers/WebManager.java
index 55a8d6e08..547f5ceb9 100644
--- a/src/main/java/world/bentobox/bentobox/managers/WebManager.java
+++ b/src/main/java/world/bentobox/bentobox/managers/WebManager.java
@@ -1,6 +1,5 @@
package world.bentobox.bentobox.managers;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
@@ -9,6 +8,8 @@
import java.util.Map;
import java.util.Optional;
+import javax.xml.bind.DatatypeConverter;
+
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
@@ -17,17 +18,18 @@
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
-import io.github.TheBusyBiscuit.GitHubWebAPI4Java.GitHubWebAPI;
-import io.github.TheBusyBiscuit.GitHubWebAPI4Java.objects.repositories.GitHubContributor;
-import io.github.TheBusyBiscuit.GitHubWebAPI4Java.objects.repositories.GitHubRepository;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.Settings;
+import world.bentobox.bentobox.api.github.GitHubWebAPI;
+import world.bentobox.bentobox.api.github.objects.repositories.GitHubContributor;
+import world.bentobox.bentobox.api.github.objects.repositories.GitHubRepository;
import world.bentobox.bentobox.web.catalog.CatalogEntry;
import world.bentobox.bentobox.web.credits.Contributor;
/**
* Handles web-related stuff.
* Should be instantiated after all addons are loaded.
+ *
* @author Poslovitch
* @since 1.3.0
*/
@@ -100,10 +102,8 @@ public void requestGitHubData() {
List repositories = new ArrayList<>();
// Gather all the repositories of installed addons.
repositories.add("BentoBoxWorld/BentoBox");
- repositories.addAll(plugin.getAddonsManager().getEnabledAddons()
- .stream().map(addon -> addon.getDescription().getRepository())
- .filter(repo -> !repo.isEmpty())
- .toList());
+ repositories.addAll(plugin.getAddonsManager().getEnabledAddons().stream()
+ .map(addon -> addon.getDescription().getRepository()).filter(repo -> !repo.isEmpty()).toList());
/* Download the contributors */
if (plugin.getSettings().isLogGithubDownloadData()) {
@@ -116,7 +116,8 @@ public void requestGitHubData() {
repo = new GitHubRepository(gh, repository);
} catch (Exception e) {
if (plugin.getSettings().isLogGithubDownloadData()) {
- plugin.logError("An unhandled exception occurred when gathering contributors data from the '" + repository + "' repository...");
+ plugin.logError("An unhandled exception occurred when gathering contributors data from the '"
+ + repository + "' repository...");
plugin.logStacktrace(e);
}
repo = null;
@@ -126,7 +127,8 @@ public void requestGitHubData() {
}
}
- // People were concerned that the download took ages, so we need to tell them it's over now.
+ // People were concerned that the download took ages, so we need to tell them
+ // it's over now.
if (plugin.getSettings().isLogGithubDownloadData()) {
plugin.log("Successfully downloaded data from GitHub.");
}
@@ -176,8 +178,10 @@ private void parseCatalogContent(String tagsContent, String topicsContent, Strin
this.addonsCatalog.clear();
this.gamemodesCatalog.clear();
- catalog.getAsJsonArray("gamemodes").forEach(gamemode -> gamemodesCatalog.add(new CatalogEntry(gamemode.getAsJsonObject())));
- catalog.getAsJsonArray("addons").forEach(addon -> addonsCatalog.add(new CatalogEntry(addon.getAsJsonObject())));
+ catalog.getAsJsonArray("gamemodes")
+ .forEach(gamemode -> gamemodesCatalog.add(new CatalogEntry(gamemode.getAsJsonObject())));
+ catalog.getAsJsonArray("addons")
+ .forEach(addon -> addonsCatalog.add(new CatalogEntry(addon.getAsJsonObject())));
} catch (JsonParseException e) {
if (plugin.getSettings().isLogGithubDownloadData()) {
plugin.log("Could not update the Catalog content: the gathered JSON data is malformed.");
@@ -186,25 +190,13 @@ private void parseCatalogContent(String tagsContent, String topicsContent, Strin
}
}
- /**
- *
- * @param repo - Github repository
- * @param fileName - file name
- * @return content or nothing
- * @since 1.8.0
- */
@NonNull
private String getContent(@NonNull GitHubRepository repo, String fileName) {
try {
- String content = repo.getContent(fileName).getContent().replaceAll("\\n", ""); // replaceAll is required here
- return new String(Base64.getDecoder().decode(content), StandardCharsets.UTF_8);
- } catch (IllegalAccessException e) {
- // Fail silently
+ String content = repo.getContent(fileName).getContent();
+ return new String(DatatypeConverter.parseBase64Binary(content.replaceAll("_", "/")));
} catch (Exception e) {
- if (plugin.getSettings().isLogGithubDownloadData()) {
- plugin.logError("An unhandled exception occurred when downloading '" + fileName + "' from GitHub...");
- plugin.logStacktrace(e);
- }
+ // Silently fail
}
return "";
}
@@ -213,16 +205,18 @@ private void gatherContributors(@NonNull GitHubRepository repo) {
try {
List addonContributors = new LinkedList<>();
for (GitHubContributor gitHubContributor : repo.getContributors()) {
- addonContributors.add(new Contributor(gitHubContributor.getUsername(), gitHubContributor.getContributionsAmount()));
+ addonContributors.add(
+ new Contributor(gitHubContributor.getUsername(), gitHubContributor.getContributionsAmount()));
}
contributors.put(repo.getFullName(), addonContributors);
- } catch (IllegalAccessException e) {
+ } catch (Exception e) {
// Silently fail
}
}
/**
* Returns the contents of the addons catalog (may be an empty list).
+ *
* @return the contents of the addons catalog.
* @since 1.5.0
*/
@@ -233,6 +227,7 @@ public List getAddonsCatalog() {
/**
* Returns the contents of the gamemodes catalog (may be an empty list).
+ *
* @return the contents of the gamemodes catalog.
* @since 1.5.0
*/
@@ -253,7 +248,9 @@ public List getContributors(String repository) {
}
/**
- * Returns an optional that may contain the {@link GitHubWebAPI} instance only and only if {@link Settings#isGithubDownloadData()} is {@code true}.
+ * Returns an optional that may contain the {@link GitHubWebAPI} instance only
+ * and only if {@link Settings#isGithubDownloadData()} is {@code true}.
+ *
* @return the GitHub instance.
* @since 1.5.0
*/
diff --git a/src/test/java/world/bentobox/bentobox/listeners/PanelListenerManagerTest.java b/src/test/java/world/bentobox/bentobox/listeners/PanelListenerManagerTest.java
index 656826e90..6da0b43d9 100644
--- a/src/test/java/world/bentobox/bentobox/listeners/PanelListenerManagerTest.java
+++ b/src/test/java/world/bentobox/bentobox/listeners/PanelListenerManagerTest.java
@@ -29,6 +29,8 @@
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MenuType;
+import org.jetbrains.annotations.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -80,6 +82,7 @@ public class PanelListenerManagerTest {
/**
*/
+ @SuppressWarnings("deprecation")
@Before
public void setUp() throws Exception {
// Set up plugin
@@ -250,6 +253,12 @@ public void open() {
}
+ @Override
+ public @Nullable MenuType getMenuType() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
}
@After
diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java
index ffd5325f7..5a62def77 100644
--- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java
+++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java
@@ -25,10 +25,8 @@
import org.bukkit.entity.Player;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
-import org.bukkit.event.player.PlayerRespawnEvent.RespawnFlag;
import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason;
import org.bukkit.inventory.ItemStack;
-import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -40,9 +38,6 @@
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-
import io.papermc.paper.ServerBuildInfo;
import world.bentobox.bentobox.AbstractCommonSetup;
import world.bentobox.bentobox.BentoBox;
@@ -237,11 +232,8 @@ public void testOnPlayerRespawn() {
when(location.clone()).thenReturn(location); // Event clones the location
// Has island
when(im.hasIsland(any(), any(UUID.class))).thenReturn(true);
- @NotNull
- Builder respawnFlags = new Builder().add(RespawnFlag.BED_SPAWN);
// Respawn
- PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, false, RespawnReason.DEATH,
- respawnFlags);
+ PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, false, RespawnReason.DEATH);
l.onPlayerRespawn(ev);
assertEquals(safeLocation, ev.getRespawnLocation());
// Verify commands
@@ -258,10 +250,11 @@ public void testOnPlayerRespawnWithoutDeath() {
IslandRespawnListener l = new IslandRespawnListener();
Location location = mock(Location.class);
when(location.getWorld()).thenReturn(world);
+ when(location.clone()).thenReturn(location);
// Has island
when(im.hasIsland(any(), any(UUID.class))).thenReturn(true);
// Respawn
- PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH);
+ PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, false, RespawnReason.DEATH);
l.onPlayerRespawn(ev);
assertEquals(location, ev.getRespawnLocation());
}
@@ -280,10 +273,11 @@ public void testOnPlayerRespawnWrongWorld() {
l.onPlayerDeath(e);
Location location = mock(Location.class);
when(location.getWorld()).thenReturn(world);
+ when(location.clone()).thenReturn(location);
// Has island
when(im.hasIsland(any(), any(UUID.class))).thenReturn(true);
// Respawn
- PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH);
+ PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, false, RespawnReason.DEATH);
l.onPlayerRespawn(ev);
assertEquals(location, ev.getRespawnLocation());
}
@@ -302,10 +296,11 @@ public void testOnPlayerRespawnFlagNotSet() {
l.onPlayerDeath(e);
Location location = mock(Location.class);
when(location.getWorld()).thenReturn(world);
+ when(location.clone()).thenReturn(location);
// Has island
when(im.hasIsland(any(), any(UUID.class))).thenReturn(true);
// Respawn
- PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, RespawnReason.DEATH);
+ PlayerRespawnEvent ev = new PlayerRespawnEvent(player, location, false, false, false, RespawnReason.DEATH);
l.onPlayerRespawn(ev);
assertEquals(location, ev.getRespawnLocation());
}
diff --git a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java
index 25be63574..0b7153811 100644
--- a/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java
+++ b/src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/RemoveMobsListenerTest.java
@@ -91,6 +91,8 @@ public void setUp() throws Exception {
// Teleports are from far away
when(inside.distanceSquared(any())).thenReturn(100D);
+ when(inside.clone()).thenReturn(inside);
+
Optional opIsland = Optional.ofNullable(island);
when(im.getProtectedIslandAt(Mockito.eq(inside))).thenReturn(opIsland);
@@ -203,7 +205,7 @@ public void testOnUserTeleportToNotIsland() {
*/
@Test
public void testOnUserRespawn() {
- PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, RespawnReason.DEATH);
+ PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, false, RespawnReason.DEATH);
new RemoveMobsListener().onUserRespawn(e);
verify(scheduler).runTask(any(), any(Runnable.class));
}
@@ -214,7 +216,8 @@ public void testOnUserRespawn() {
@Test
public void testOnUserRespawnDoNotRemove() {
Flags.REMOVE_MOBS.setSetting(world, false);
- PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, RespawnReason.DEATH);
+
+ PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, false, RespawnReason.DEATH);
new RemoveMobsListener().onUserRespawn(e);
verify(scheduler, never()).runTask(any(), any(Runnable.class));
}
@@ -226,7 +229,7 @@ public void testOnUserRespawnDoNotRemove() {
public void testOnUserRespawnNotIsland() {
// Not on island
when(im.locationIsOnIsland(any(), any())).thenReturn(false);
- PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, RespawnReason.DEATH);
+ PlayerRespawnEvent e = new PlayerRespawnEvent(player, inside, false, false, false, RespawnReason.DEATH);
new RemoveMobsListener().onUserRespawn(e);
verify(scheduler, never()).runTask(any(), any(Runnable.class));
}
diff --git a/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java b/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java
index 5cd0ab4f8..ab2a9491b 100644
--- a/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java
+++ b/src/test/java/world/bentobox/bentobox/util/ItemParserTest.java
@@ -143,6 +143,12 @@ public int size() {
// TODO Auto-generated method stub
return 0;
}
+
+ @Override
+ public Stream keyStream() {
+ // TODO Auto-generated method stub
+ return null;
+ }
}
@After