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