Merge pull request #2537 from BentoBoxWorld/develop

Release 2.7.0
This commit is contained in:
tastybento 2024-10-26 17:05:38 -07:00 committed by GitHub
commit d29eb8824c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 1308 additions and 442 deletions

View File

@ -73,10 +73,10 @@
<postgresql.version>42.2.18</postgresql.version> <postgresql.version>42.2.18</postgresql.version>
<hikaricp.version>5.0.1</hikaricp.version> <hikaricp.version>5.0.1</hikaricp.version>
<!-- More visible way to change dependency versions --> <!-- More visible way to change dependency versions -->
<spigot.version>1.20.5-R0.1-SNAPSHOT</spigot.version> <spigot.version>1.21.3-R0.1-SNAPSHOT</spigot.version>
<!-- Might differ from the last Spigot release for short periods <!-- Might differ from the last Spigot release for short periods
of time --> of time -->
<paper.version>1.20.6-R0.1-SNAPSHOT</paper.version> <paper.version>1.21.1-R0.1-SNAPSHOT</paper.version>
<bstats.version>3.0.0</bstats.version> <bstats.version>3.0.0</bstats.version>
<vault.version>1.7.1</vault.version> <vault.version>1.7.1</vault.version>
<placeholderapi.version>2.10.9</placeholderapi.version> <placeholderapi.version>2.10.9</placeholderapi.version>
@ -88,7 +88,7 @@
<!-- Do not change unless you want different name for local builds. --> <!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number> <build.number>-LOCAL</build.number>
<!-- This allows to change between versions. --> <!-- This allows to change between versions. -->
<build.version>2.6.0</build.version> <build.version>2.7.0</build.version>
<sonar.organization>bentobox-world</sonar.organization> <sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url> <sonar.host.url>https://sonarcloud.io</sonar.host.url>
<server.jars>${project.basedir}/lib</server.jars> <server.jars>${project.basedir}/lib</server.jars>

View File

@ -1,7 +1,5 @@
package world.bentobox.bentobox; package world.bentobox.bentobox;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -209,6 +207,7 @@ public class BentoBox extends JavaPlugin implements Listener {
registerListeners(); registerListeners();
// Load islands from database - need to wait until all the worlds are loaded // Load islands from database - need to wait until all the worlds are loaded
log("Loading islands from database...");
try { try {
islandsManager.load(); islandsManager.load();
} catch (Exception e) { } catch (Exception e) {
@ -466,16 +465,6 @@ public class BentoBox extends JavaPlugin implements Listener {
return false; return false;
} }
log("Saving default panels...");
if (!Files.exists(Path.of(this.getDataFolder().getPath(), "panels", "island_creation_panel.yml"))) {
log("Saving default island_creation_panel...");
this.saveResource("panels/island_creation_panel.yml", false);
}
if (!Files.exists(Path.of(this.getDataFolder().getPath(), "panels", "language_panel.yml"))) {
log("Saving default language_panel...");
this.saveResource("panels/language_panel.yml", false);
}
return true; return true;
} }

View File

@ -346,7 +346,7 @@ public abstract class CompositeCommand extends Command implements PluginIdentifi
* *
* @return IslandsManager * @return IslandsManager
*/ */
protected IslandsManager getIslands() { public IslandsManager getIslands() {
return plugin.getIslands(); return plugin.getIslands();
} }

View File

@ -1,10 +1,11 @@
package world.bentobox.bentobox.api.commands.admin.purge; package world.bentobox.bentobox.api.commands.admin.purge;
import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -17,16 +18,19 @@ import world.bentobox.bentobox.api.events.island.IslandDeletedEvent;
import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
public class AdminPurgeCommand extends CompositeCommand implements Listener { public class AdminPurgeCommand extends CompositeCommand implements Listener {
private static final Long YEAR2000 = 946713600L;
private static final int TOO_MANY = 1000;
private int count; private int count;
private boolean inPurge; private boolean inPurge;
private boolean scanning;
private boolean toBeConfirmed; private boolean toBeConfirmed;
private Iterator<String> it; private Iterator<String> it;
private User user; private User user;
private Set<String> islands = new HashSet<>(); private Set<String> islands = new HashSet<>();
private Set<Integer> loggedTiers = new HashSet<>(); // Set to store logged percentage tiers
public AdminPurgeCommand(CompositeCommand parent) { public AdminPurgeCommand(CompositeCommand parent) {
super(parent, "purge"); super(parent, "purge");
@ -47,6 +51,10 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
@Override @Override
public boolean canExecute(User user, String label, List<String> args) { public boolean canExecute(User user, String label, List<String> args) {
if (scanning) {
user.sendMessage("commands.admin.purge.scanning-in-progress");
return false;
}
if (inPurge) { if (inPurge) {
user.sendMessage("commands.admin.purge.purge-in-progress", TextVariables.LABEL, this.getTopLabel()); user.sendMessage("commands.admin.purge.purge-in-progress", TextVariables.LABEL, this.getTopLabel());
return false; return false;
@ -75,13 +83,25 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
user.sendMessage("commands.admin.purge.days-one-or-more"); user.sendMessage("commands.admin.purge.days-one-or-more");
return false; return false;
} }
islands = getOldIslands(days); user.sendMessage("commands.admin.purge.scanning");
user.sendMessage("commands.admin.purge.purgable-islands", TextVariables.NUMBER, String.valueOf(islands.size())); scanning = true;
if (!islands.isEmpty()) { getOldIslands(days).thenAccept(islandSet -> {
user.sendMessage("commands.admin.purge.purgable-islands", TextVariables.NUMBER,
String.valueOf(islandSet.size()));
if (islandSet.size() > TOO_MANY
&& !BentoBox.getInstance().getSettings().isKeepPreviousIslandOnReset()) {
user.sendMessage("commands.admin.purge.too-many"); // Give warning
}
if (!islandSet.isEmpty()) {
toBeConfirmed = true; toBeConfirmed = true;
user.sendMessage("commands.admin.purge.confirm", TextVariables.LABEL, this.getTopLabel()); user.sendMessage("commands.admin.purge.confirm", TextVariables.LABEL, this.getTopLabel());
return false; islands = islandSet;
} else {
user.sendMessage("commands.admin.purge.none-found");
} }
scanning = false;
});
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
user.sendMessage("commands.admin.purge.number-error"); user.sendMessage("commands.admin.purge.number-error");
return false; return false;
@ -94,6 +114,7 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
user.sendMessage("commands.admin.purge.see-console-for-status", TextVariables.LABEL, this.getTopLabel()); user.sendMessage("commands.admin.purge.see-console-for-status", TextVariables.LABEL, this.getTopLabel());
it = islands.iterator(); it = islands.iterator();
count = 0; count = 0;
loggedTiers.clear(); // % reporting
// Delete first island // Delete first island
deleteIsland(); deleteIsland();
} }
@ -103,8 +124,21 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
getIslands().getIslandById(it.next()).ifPresent(i -> { getIslands().getIslandById(it.next()).ifPresent(i -> {
getIslands().deleteIsland(i, true, null); getIslands().deleteIsland(i, true, null);
count++; count++;
String percentage = String.format("%.1f", (((float) count)/getPurgeableIslandsCount() * 100)); float percentage = ((float) count / getPurgeableIslandsCount()) * 100;
getPlugin().log(count + " islands purged out of " + getPurgeableIslandsCount() + " (" + percentage + " %)"); String percentageStr = String.format("%.1f", percentage);
// Round the percentage to check for specific tiers
int roundedPercentage = (int) Math.floor(percentage);
// Determine if this percentage should be logged: 1%, 5%, or any new multiple of 5%
if (!BentoBox.getInstance().getSettings().isKeepPreviousIslandOnReset() || (roundedPercentage > 0
&& (roundedPercentage == 1 || roundedPercentage == 5 || roundedPercentage % 5 == 0)
&& !loggedTiers.contains(roundedPercentage))) {
// Log the message and add the tier to the logged set
getPlugin().log(count + " islands purged out of " + getPurgeableIslandsCount() + " ("
+ percentageStr + " %)");
loggedTiers.add(roundedPercentage);
}
}); });
} else { } else {
user.sendMessage("commands.admin.purge.completed"); user.sendMessage("commands.admin.purge.completed");
@ -116,7 +150,8 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
void onIslandDeleted(IslandDeletedEvent e) { void onIslandDeleted(IslandDeletedEvent e) {
if (inPurge) { if (inPurge) {
deleteIsland(); // Run after one tick - you cannot run millions of events in one tick otherwise the server shuts down
Bukkit.getScheduler().runTaskLater(getPlugin(), () -> deleteIsland(), 2L); // 10 a second
} }
} }
@ -125,40 +160,46 @@ public class AdminPurgeCommand extends CompositeCommand implements Listener {
* @param days days * @param days days
* @return set of islands * @return set of islands
*/ */
Set<String> getOldIslands(int days) { CompletableFuture<Set<String>> getOldIslands(int days) {
long currentTimeMillis = System.currentTimeMillis(); CompletableFuture<Set<String>> result = new CompletableFuture<>();
long daysInMilliseconds = (long) days * 1000 * 3600 * 24;
Set<String> oldIslands = new HashSet<>();
// Process islands in one pass, logging and adding to the set if applicable // Process islands in one pass, logging and adding to the set if applicable
getPlugin().getIslands().getIslands().stream() getPlugin().getIslands().getIslandsASync().thenAccept(list -> {
user.sendMessage("commands.admin.purge.total-islands", TextVariables.NUMBER, String.valueOf(list.size()));
Set<String> oldIslands = new HashSet<>();
list.stream()
.filter(i -> !i.isSpawn()).filter(i -> !i.getPurgeProtected()) .filter(i -> !i.isSpawn()).filter(i -> !i.getPurgeProtected())
.filter(i -> i.getWorld() != null) // to handle currently unloaded world islands .filter(i -> i.getWorld() != null) // to handle currently unloaded world islands
.filter(i -> i.getWorld().equals(this.getWorld())).filter(Island::isOwned).filter( .filter(i -> i.getWorld().equals(this.getWorld())) // Island needs to be in this world
i -> i.getMemberSet().stream() .filter(Island::isOwned) // The island needs to be owned
.allMatch(member -> (currentTimeMillis .filter(i -> i.getMemberSet().stream().allMatch(member -> checkLastLoginTimestamp(days, member)))
- Bukkit.getOfflinePlayer(member).getLastPlayed()) > daysInMilliseconds)) .forEach(i -> oldIslands.add(i.getUniqueId())); // Add the unique island ID to the set
.forEach(i -> {
// Add the unique island ID to the set
oldIslands.add(i.getUniqueId());
BentoBox.getInstance().log("Will purge island at " + Util.xyz(i.getCenter().toVector()) + " in "
+ i.getWorld().getName());
// Log each member's last login information
i.getMemberSet().forEach(member -> {
Date lastLogin = new Date(Bukkit.getOfflinePlayer(member).getLastPlayed());
BentoBox.getInstance()
.log("Player " + BentoBox.getInstance().getPlayers().getName(member)
+ " last logged in "
+ (int) ((currentTimeMillis - Bukkit.getOfflinePlayer(member).getLastPlayed())
/ 1000 / 3600 / 24)
+ " days ago. " + lastLogin);
});
BentoBox.getInstance().log("+-----------------------------------------+");
});
return oldIslands; result.complete(oldIslands);
});
return result;
} }
private boolean checkLastLoginTimestamp(int days, UUID member) {
long daysInMilliseconds = days * 24L * 3600 * 1000; // Calculate days in milliseconds
Long lastLoginTimestamp = getPlayers().getLastLoginTimestamp(member);
// If no valid last login time is found or it's before the year 2000, try to fetch from Bukkit
if (lastLoginTimestamp == null || lastLoginTimestamp < YEAR2000) {
lastLoginTimestamp = Bukkit.getOfflinePlayer(member).getLastPlayed();
// If still invalid, set the current timestamp to mark the user for eventual purging
if (lastLoginTimestamp < YEAR2000) {
getPlayers().setLoginTimeStamp(member, System.currentTimeMillis());
return false; // User will be purged in the future
} else {
// Otherwise, update the last login timestamp with the valid value from Bukkit
getPlayers().setLoginTimeStamp(member, lastLoginTimestamp);
}
}
// Check if the difference between now and the last login is greater than the allowed days
return System.currentTimeMillis() - lastLoginTimestamp > daysInMilliseconds;
}
/** /**
* @return the inPurge * @return the inPurge
*/ */

View File

@ -61,9 +61,9 @@ public class IslandGoCommand extends DelayedTeleportCommand {
@Override @Override
public boolean execute(User user, String label, List<String> args) { public boolean execute(User user, String label, List<String> args) {
Map<String, IslandInfo> names = getNameIslandMap(user);
// Check if the home is known // Check if the home is known
if (!args.isEmpty()) { if (!args.isEmpty()) {
Map<String, IslandInfo> names = getNameIslandMap(user);
final String name = String.join(" ", args); final String name = String.join(" ", args);
if (!names.containsKey(name)) { if (!names.containsKey(name)) {
// Failed home name check // Failed home name check
@ -113,7 +113,11 @@ public class IslandGoCommand extends DelayedTeleportCommand {
} }
private record IslandInfo(Island island, boolean islandName) {} /**
* Record of islands and the name to type
*/
private record IslandInfo(Island island, boolean islandName) {
}
private Map<String, IslandInfo> getNameIslandMap(User user) { private Map<String, IslandInfo> getNameIslandMap(User user) {
Map<String, IslandInfo> islandMap = new HashMap<>(); Map<String, IslandInfo> islandMap = new HashMap<>();
@ -129,7 +133,8 @@ public class IslandGoCommand extends DelayedTeleportCommand {
islandMap.put(text, new IslandInfo(island, true)); islandMap.put(text, new IslandInfo(island, true));
} }
// Add homes. Homes do not need an island specified // Add homes. Homes do not need an island specified
island.getHomes().keySet().forEach(n -> islandMap.put(n, new IslandInfo(island, false))); island.getHomes().keySet().stream().filter(n -> !n.isBlank())
.forEach(n -> islandMap.put(n, new IslandInfo(island, false)));
} }
return islandMap; return islandMap;

View File

@ -1,19 +1,12 @@
package world.bentobox.bentobox.api.commands.island; package world.bentobox.bentobox.api.commands.island;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.panels.customizable.IslandHomesPanel;
import world.bentobox.bentobox.util.Util;
public class IslandHomesCommand extends ConfirmableCommand { public class IslandHomesCommand extends CompositeCommand {
private List<Island> islands;
public IslandHomesCommand(CompositeCommand islandCommand) { public IslandHomesCommand(CompositeCommand islandCommand) {
super(islandCommand, "homes"); super(islandCommand, "homes");
@ -28,9 +21,8 @@ public class IslandHomesCommand extends ConfirmableCommand {
@Override @Override
public boolean canExecute(User user, String label, List<String> args) { public boolean canExecute(User user, String label, List<String> args) {
islands = getIslands().getIslands(getWorld(), user);
// Check island // Check island
if (islands.isEmpty()) { if (getIslands().getIslands(getWorld(), user).isEmpty()) {
user.sendMessage("general.errors.no-island"); user.sendMessage("general.errors.no-island");
return false; return false;
} }
@ -39,22 +31,8 @@ public class IslandHomesCommand extends ConfirmableCommand {
@Override @Override
public boolean execute(User user, String label, List<String> args) { public boolean execute(User user, String label, List<String> args) {
user.sendMessage("commands.island.sethome.homes-are"); IslandHomesPanel.openPanel(this, user);
islands.forEach(island ->
island.getHomes().keySet().stream().filter(s -> !s.isEmpty())
.forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s)));
return true; return true;
} }
@Override
public Optional<List<String>> tabComplete(User user, String alias, List<String> args) {
String lastArg = !args.isEmpty() ? args.get(args.size()-1) : "";
List<String> result = new ArrayList<>();
for (Island island : getIslands().getIslands(getWorld(), user.getUniqueId())) {
result.addAll(island.getHomes().keySet());
}
return Optional.of(Util.tabLimit(result, lastArg));
}
} }

View File

@ -362,9 +362,8 @@ public class TemplatedPanel extends Panel {
* this button is present. * this button is present.
* *
* @return Map that links button type to amount in the gui. * @return Map that links button type to amount in the gui.
* @deprecated Use {@link #amount(String)} instead. * Use {@link #amount(String)} instead.
*/ */
@Deprecated
public Map<String, Integer> amountMap() { public Map<String, Integer> amountMap() {
return this.parentPanel.typeSlotMap; return this.parentPanel.typeSlotMap;
} }

View File

@ -90,15 +90,29 @@ public class TemplateReader
} }
File file = new File(panelLocation, templateName.endsWith(YML) ? templateName : templateName + YML); File file = new File(panelLocation, templateName.endsWith(YML) ? templateName : templateName + YML);
String absolutePath = file.getAbsolutePath();
if (!file.exists()) if (!file.exists())
{ {
// Try to get it from the JAR
String keyword = "panels/";
// Find the index of the keyword "panels/"
int index = absolutePath.indexOf(keyword);
// If the keyword is found, extract the substring starting from that index
if (index != -1) {
BentoBox.getInstance().saveResource(absolutePath.substring(index), false);
file = new File(panelLocation, templateName.endsWith(YML) ? templateName : templateName + YML);
} else {
BentoBox.getInstance().logError(file.getAbsolutePath() + " does not exist for panel template"); BentoBox.getInstance().logError(file.getAbsolutePath() + " does not exist for panel template");
// Return as file does not exist. // Return as file does not exist.
return null; return null;
} }
final String panelKey = file.getAbsolutePath() + ":" + panelName; }
final String panelKey = absolutePath + ":" + panelName;
// Check if panel is already crafted. // Check if panel is already crafted.
if (TemplateReader.loadedPanels.containsKey(panelKey)) if (TemplateReader.loadedPanels.containsKey(panelKey))

View File

@ -632,7 +632,7 @@ public class User implements MetaDataAble {
// Add any text before the current match // Add any text before the current match
if (matcher.start() > lastMatchEnd) { if (matcher.start() > lastMatchEnd) {
String beforeMatch = message.substring(lastMatchEnd, matcher.start()); String beforeMatch = message.substring(lastMatchEnd, matcher.start());
baseComponent.addExtra(new TextComponent(beforeMatch)); baseComponent.addExtra(TextComponent.fromLegacy(beforeMatch));
} }
// Check if it's a recognized command or an unknown bracketed text // Check if it's a recognized command or an unknown bracketed text
@ -658,12 +658,12 @@ public class User implements MetaDataAble {
break; break;
default: default:
// Unrecognized command; preserve it in the output text // Unrecognized command; preserve it in the output text
baseComponent.addExtra(new TextComponent(matcher.group(0))); baseComponent.addExtra(TextComponent.fromLegacy(matcher.group(0)));
} }
} else if (matcher.group(3) != null) { } else if (matcher.group(3) != null) {
// Unrecognized bracketed text; preserve it in the output // Unrecognized bracketed text; preserve it in the output
baseComponent.addExtra(new TextComponent("[[" + matcher.group(3) + "]]")); baseComponent.addExtra(TextComponent.fromLegacy("[[" + matcher.group(3) + "]]"));
} }
// Update the last match end position // Update the last match end position
@ -673,7 +673,7 @@ public class User implements MetaDataAble {
// Add any remaining text after the last match // Add any remaining text after the last match
if (lastMatchEnd < message.length()) { if (lastMatchEnd < message.length()) {
String remainingText = message.substring(lastMatchEnd); String remainingText = message.substring(lastMatchEnd);
baseComponent.addExtra(new TextComponent(remainingText)); baseComponent.addExtra(TextComponent.fromLegacy(remainingText));
} }
// Apply the first encountered ClickEvent and HoverEvent to the entire message // Apply the first encountered ClickEvent and HoverEvent to the entire message

View File

@ -134,10 +134,31 @@ public abstract class AbstractDatabaseHandler<T> {
@Nullable @Nullable
public abstract T loadObject(@NonNull String uniqueId) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException; public abstract T loadObject(@NonNull String uniqueId) throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IntrospectionException, NoSuchMethodException;
/**
* Loads all the records in this table and returns a list of them async
* @return CompletableFuture List of <T>
* @since 2.7.0
*/
public CompletableFuture<List<T>> loadObjectsASync() {
CompletableFuture<List<T>> completableFuture = new CompletableFuture<>();
Bukkit.getScheduler().runTaskAsynchronously(BentoBox.getInstance(), () -> {
try {
completableFuture.complete(loadObjects()); // Complete the future with the result
} catch (Exception e) {
completableFuture.completeExceptionally(e); // Complete exceptionally if an error occurs
plugin.logError("Failed to load objects asynchronously: " + e.getMessage());
}
});
return completableFuture;
}
/** /**
* Save T into the corresponding database * Save T into the corresponding database
* *
* @param instance that should be inserted into the database * @param instance that should be inserted into the database
* @return completable future that is true if saved
*/ */
public abstract CompletableFuture<Boolean> saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException ; public abstract CompletableFuture<Boolean> saveObject(T instance) throws IllegalAccessException, InvocationTargetException, IntrospectionException ;

View File

@ -166,6 +166,13 @@ public class Database<T> {
return dataObjects; return dataObjects;
} }
/**
* Load all objects async
* @return CompletableFuture<List<T>>
*/
public @NonNull CompletableFuture<List<T>> loadObjectsASync() {
return handler.loadObjectsASync();
}
} }

View File

@ -19,6 +19,7 @@ import java.util.stream.Collectors;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -1512,7 +1513,7 @@ public class Island implements DataObject, MetaDataAble {
*/ */
public boolean hasNetherIsland() { public boolean hasNetherIsland() {
World nether = BentoBox.getInstance().getIWM().getNetherWorld(getWorld()); World nether = BentoBox.getInstance().getIWM().getNetherWorld(getWorld());
return nether != null && !getCenter().toVector().toLocation(nether).getBlock().getType().isAir(); return nether != null && (getCenter().toVector().toLocation(nether).getBlock().getType() != Material.AIR);
} }
/** /**
@ -1536,7 +1537,7 @@ public class Island implements DataObject, MetaDataAble {
*/ */
public boolean hasEndIsland() { public boolean hasEndIsland() {
World end = BentoBox.getInstance().getIWM().getEndWorld(getWorld()); World end = BentoBox.getInstance().getIWM().getEndWorld(getWorld());
return end != null && !getCenter().toVector().toLocation(end).getBlock().getType().isAir(); return end != null && (getCenter().toVector().toLocation(end).getBlock().getType() != Material.AIR);
} }
/** /**

View File

@ -10,6 +10,7 @@ import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.Expose; import com.google.gson.annotations.Expose;
@ -37,6 +38,8 @@ public class Players implements DataObject, MetaDataAble {
private String locale = ""; private String locale = "";
@Expose @Expose
private Map<String, Integer> deaths = new HashMap<>(); private Map<String, Integer> deaths = new HashMap<>();
@Expose
private Long lastLogin;
/** /**
* This variable stores set of worlds where user inventory must be cleared. * This variable stores set of worlds where user inventory must be cleared.
@ -292,5 +295,20 @@ public class Players implements DataObject, MetaDataAble {
this.metaData = metaData; this.metaData = metaData;
} }
/**
* @return the lastLogin, Unix timestamp, or null if never logged in since this was tracked
* @since 2.6.0
*/
@Nullable
public Long getLastLogin() {
return lastLogin;
}
/**
* @param lastLogin the lastLogin to set
*/
public void setLastLogin(Long lastLogin) {
this.lastLogin = lastLogin;
}
} }

View File

@ -64,6 +64,9 @@ public class JoinLeaveListener implements Listener {
// don't exist // don't exist
players.getPlayer(playerUUID); players.getPlayer(playerUUID);
// Set the login
players.setLoginTimeStamp(user);
// Reset island resets if required // Reset island resets if required
plugin.getIWM().getOverWorlds().stream() plugin.getIWM().getOverWorlds().stream()
.filter(w -> event.getPlayer().getLastPlayed() < plugin.getIWM().getResetEpoch(w)) .filter(w -> event.getPlayer().getLastPlayed() < plugin.getIWM().getResetEpoch(w))

View File

@ -293,6 +293,9 @@ public class IslandsManager {
plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(id); plugin.getIslandDeletionManager().getIslandChunkDeletionManager().add(id);
// Tell other servers // Tell other servers
MultiLib.notify("bentobox-deleteIsland", getGson().toJson(id)); MultiLib.notify("bentobox-deleteIsland", getGson().toJson(id));
} else {
// Fire the deletion event immediately
IslandEvent.builder().deletedIslandInfo(new IslandDeletion(island)).reason(Reason.DELETED).build();
} }
// Delete the island from the database // Delete the island from the database
handler.deleteObject(island); handler.deleteObject(island);
@ -442,6 +445,10 @@ public class IslandsManager {
: Optional.empty(); : Optional.empty();
} }
public boolean isIslandAt(@NonNull Location location) {
return plugin.getIWM().inWorld(location) ? islandCache.isIslandAt(location) : false;
}
/** /**
* Returns an <strong>unmodifiable collection</strong> of all existing islands * Returns an <strong>unmodifiable collection</strong> of all existing islands
* (even those who may be unowned). * (even those who may be unowned).
@ -454,6 +461,16 @@ public class IslandsManager {
return handler.loadObjects().stream().toList(); return handler.loadObjects().stream().toList();
} }
/**
* Loads all existing islands from the database without caching async
*
* @return CompletableFuture<List> of every island
* @since 2.7.0
*/
public CompletableFuture<List<Island>> getIslandsASync() {
return handler.loadObjectsASync();
}
/** /**
* Returns an <strong>unmodifiable collection</strong> of all the islands (even * Returns an <strong>unmodifiable collection</strong> of all the islands (even
* those who may be unowned) in the specified world. * those who may be unowned) in the specified world.

View File

@ -420,4 +420,44 @@ public class PlayersManager {
return CompletableFuture.completedFuture(false); return CompletableFuture.completedFuture(false);
} }
/**
* Records when the user last logged in. Called by the joinleave listener
* @param user user
* @since 2.7.0
*/
public void setLoginTimeStamp(User user) {
if (user.isPlayer() && user.isOnline()) {
setLoginTimeStamp(user.getUniqueId(), System.currentTimeMillis());
}
}
/**
* Set the player's last login time to a timestamp
* @param playerUUID player UUID
* @param timestamp timestamp to set
* @since 2.7.0
*/
public void setLoginTimeStamp(UUID playerUUID, long timestamp) {
Players p = this.getPlayer(playerUUID);
if (p != null) {
p.setLastLogin(timestamp);
this.savePlayer(playerUUID);
}
}
/**
* Get the last login time stamp for this player
* @param uuid player's UUID
* @return timestamp or null if unknown or not recorded yet
* @since 2.7.0
*/
@Nullable
public Long getLastLoginTimestamp(UUID uuid) {
Players p = this.getPlayer(uuid);
if (p != null) {
return p.getLastLogin();
}
return null;
}
} }

View File

@ -74,7 +74,9 @@ public class DefaultNewIslandLocationStrategy implements NewIslandLocationStrate
*/ */
protected Result isIsland(Location location) { protected Result isIsland(Location location) {
// Quick check // Quick check
if (plugin.getIslands().getIslandAt(location).isPresent()) return Result.ISLAND_FOUND; if (plugin.getIslands().isIslandAt(location)) {
return Result.ISLAND_FOUND;
}
World world = location.getWorld(); World world = location.getWorld();

View File

@ -257,6 +257,23 @@ public class IslandCache {
} }
} }
/**
* Returns if there is island at the location. This includes
* the full island space, not just the protected area.
* Does not cause a database load of the island.
*
* @param location the location
* @return true if there is an island there
*/
@Nullable
public boolean isIslandAt(@NonNull Location location) {
World w = Util.getWorld(location.getWorld());
if (w == null || !grids.containsKey(w)) {
return false;
}
return grids.get(w).isIslandAt(location.getBlockX(), location.getBlockZ());
}
/** /**
* Returns the island at the location or null if there is none. This includes * Returns the island at the location or null if there is none. This includes
* the full island space, not just the protected area * the full island space, not just the protected area

View File

@ -3,6 +3,8 @@ package world.bentobox.bentobox.managers.island;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.TreeMap; import java.util.TreeMap;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
/** /**
@ -11,7 +13,11 @@ import world.bentobox.bentobox.database.objects.Island;
* *
*/ */
class IslandGrid { class IslandGrid {
private final TreeMap<Integer, TreeMap<Integer, String>> grid = new TreeMap<>();
private record IslandData(String id, int minX, int minZ, int range) {
}
private final TreeMap<Integer, TreeMap<Integer, IslandData>> grid = new TreeMap<>();
private final IslandCache im; private final IslandCache im;
/** /**
@ -29,10 +35,13 @@ class IslandGrid {
*/ */
public boolean addToGrid(Island island) { public boolean addToGrid(Island island) {
// Check if we know about this island already // Check if we know about this island already
if (grid.containsKey(island.getMinX())) { int minX = island.getMinX();
TreeMap<Integer, String> zEntry = grid.get(island.getMinX()); int minZ = island.getMinZ();
if (zEntry.containsKey(island.getMinZ())) { IslandData islandData = new IslandData(island.getUniqueId(), minX, minZ, island.getRange());
if (island.getUniqueId().equals(zEntry.get(island.getMinZ()))) { if (grid.containsKey(minX)) {
TreeMap<Integer, IslandData> zEntry = grid.get(minX);
if (zEntry.containsKey(minZ)) {
if (island.getUniqueId().equals(zEntry.get(minZ).id())) {
// If it is the same island then it's okay // If it is the same island then it's okay
return true; return true;
} }
@ -40,14 +49,14 @@ class IslandGrid {
return false; return false;
} else { } else {
// Add island // Add island
zEntry.put(island.getMinZ(), island.getUniqueId()); zEntry.put(minZ, islandData);
grid.put(island.getMinX(), zEntry); grid.put(minX, zEntry);
} }
} else { } else {
// Add island // Add island
TreeMap<Integer, String> zEntry = new TreeMap<>(); TreeMap<Integer, IslandData> zEntry = new TreeMap<>();
zEntry.put(island.getMinZ(), island.getUniqueId()); zEntry.put(minZ, islandData);
grid.put(island.getMinX(), zEntry); grid.put(minX, zEntry);
} }
return true; return true;
} }
@ -60,7 +69,7 @@ class IslandGrid {
public boolean removeFromGrid(Island island) { public boolean removeFromGrid(Island island) {
String id = island.getUniqueId(); String id = island.getUniqueId();
boolean removed = grid.values().stream() boolean removed = grid.values().stream()
.anyMatch(innerMap -> innerMap.values().removeIf(innerValue -> innerValue.equals(id))); .anyMatch(innerMap -> innerMap.values().removeIf(innerValue -> innerValue.id().equals(id)));
grid.values().removeIf(TreeMap::isEmpty); grid.values().removeIf(TreeMap::isEmpty);
@ -70,35 +79,55 @@ class IslandGrid {
/** /**
* Retrieves the island located at the specified x and z coordinates, covering both the protected area * Retrieves the island located at the specified x and z coordinates, covering both the protected area
* and the full island space. Returns null if no island exists at the given location. * and the full island space. Returns null if no island exists at the given location.
* This will load the island from the database if it is not in the cache.
* *
* @param x the x coordinate of the location * @param x the x coordinate of the location
* @param z the z coordinate of the location * @param z the z coordinate of the location
* @return the Island at the specified location, or null if no island is found * @return the Island at the specified location, or null if no island is found
*/ */
public Island getIslandAt(int x, int z) { public Island getIslandAt(int x, int z) {
String id = getIslandStringAt(x, z);
if (id == null) {
return null;
}
// Retrieve the island using the id found - loading from database if required
return im.getIslandById(id);
}
/**
* Checks if an island is at this coordinate or not
* @param x coord
* @param z coord
* @return true if there is an island registered here in the grid
*/
public boolean isIslandAt(int x, int z) {
return getIslandStringAt(x, z) != null;
}
/**
* Get the island ID string for an island at this coordinates, or null if none.
* @param x coord
* @param z coord
* @return Unique Island ID string, or null if there is no island here.
*/
public @Nullable String getIslandStringAt(int x, int z) {
// Attempt to find the closest x-coordinate entry that does not exceed 'x' // Attempt to find the closest x-coordinate entry that does not exceed 'x'
Entry<Integer, TreeMap<Integer, String>> xEntry = grid.floorEntry(x); Entry<Integer, TreeMap<Integer, IslandData>> xEntry = grid.floorEntry(x);
if (xEntry == null) { if (xEntry == null) {
return null; // No x-coordinate entry found, return null return null; // No x-coordinate entry found, return null
} }
// Attempt to find the closest z-coordinate entry that does not exceed 'z' within the found x-coordinate // Attempt to find the closest z-coordinate entry that does not exceed 'z' within the found x-coordinate
Entry<Integer, String> zEntry = xEntry.getValue().floorEntry(z); Entry<Integer, IslandData> zEntry = xEntry.getValue().floorEntry(z);
if (zEntry == null) { if (zEntry == null) {
return null; // No z-coordinate entry found, return null return null; // No z-coordinate entry found, return null
} }
// Retrieve the island using the id found in the z-coordinate entry
Island island = im.getIslandById(zEntry.getValue());
if (island == null) {
return null; // No island found by the id, return null
}
// Check if the specified coordinates are within the island space // Check if the specified coordinates are within the island space
if (island.inIslandSpace(x, z)) { if (x >= zEntry.getValue().minX() && x < (zEntry.getValue().minX() + zEntry.getValue().range() * 2)
return island; // Coordinates are within island space, return the island && z >= zEntry.getValue().minZ() && z < (zEntry.getValue().minZ() + zEntry.getValue().range() * 2)) {
return zEntry.getValue().id();
} }
// Coordinates are outside the island space, return null
return null; return null;
} }
@ -107,7 +136,7 @@ class IslandGrid {
*/ */
public long getSize() { public long getSize() {
long count = 0; long count = 0;
for (TreeMap<Integer, String> innerMap : grid.values()) { for (TreeMap<Integer, IslandData> innerMap : grid.values()) {
count += innerMap.size(); count += innerMap.size();
} }
return count; return count;

View File

@ -0,0 +1,8 @@
package world.bentobox.bentobox.nms.v1_21_2_R0_1_SNAPSHOT;
/**
* Same as 1.21
*/
public class PasteHandlerImpl extends world.bentobox.bentobox.nms.v1_21_R0_1_SNAPSHOT.PasteHandlerImpl {
// Do nothing special
}

View File

@ -0,0 +1,8 @@
package world.bentobox.bentobox.nms.v1_21_2_R0_1_SNAPSHOT;
/**
* Same as 1.21
*/
public class WorldRegeneratorImpl extends world.bentobox.bentobox.nms.v1_21_R0_1_SNAPSHOT.WorldRegeneratorImpl {
// Do nothing special
}

View File

@ -0,0 +1,52 @@
package world.bentobox.bentobox.nms.v1_21_3_R0_1_SNAPSHOT;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_21_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R2.block.data.CraftBlockData;
import net.minecraft.core.BlockPosition;
import net.minecraft.world.level.block.state.IBlockData;
import net.minecraft.world.level.chunk.Chunk;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.nms.PasteHandler;
import world.bentobox.bentobox.util.DefaultPasteUtil;
import world.bentobox.bentobox.util.Util;
public class PasteHandlerImpl implements PasteHandler {
protected static final IBlockData AIR = ((CraftBlockData) AIR_BLOCKDATA).getState();
/**
* Set the block to the location
*
* @param island - island
* @param location - location
* @param bpBlock - blueprint block
*/
@Override
public CompletableFuture<Void> setBlock(Island island, Location location, BlueprintBlock bpBlock) {
return Util.getChunkAtAsync(location).thenRun(() -> {
Block block = location.getBlock();
// Set the block data - default is AIR
BlockData bd = DefaultPasteUtil.createBlockData(bpBlock);
CraftBlockData craft = (CraftBlockData) bd;
net.minecraft.world.level.World nmsWorld = ((CraftWorld) location.getWorld()).getHandle();
Chunk nmsChunk = nmsWorld.d(location.getBlockX() >> 4, location.getBlockZ() >> 4);
BlockPosition bp = new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ());
// Setting the block to air before setting to another state prevents some console errors
nmsChunk.a(bp, AIR, false);
nmsChunk.a(bp, craft.getState(), false);
block.setBlockData(bd, false);
DefaultPasteUtil.setBlockState(island, block, bpBlock);
// Set biome
if (bpBlock.getBiome() != null) {
block.setBiome(bpBlock.getBiome());
}
});
}
}

View File

@ -0,0 +1,26 @@
package world.bentobox.bentobox.nms.v1_21_3_R0_1_SNAPSHOT;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_21_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R2.block.data.CraftBlockData;
import net.minecraft.core.BlockPosition;
import net.minecraft.world.level.World;
import net.minecraft.world.level.chunk.Chunk;
import world.bentobox.bentobox.nms.CopyWorldRegenerator;
public class WorldRegeneratorImpl extends CopyWorldRegenerator {
@Override
public void setBlockInNativeChunk(org.bukkit.Chunk chunk, int x, int y, int z, BlockData blockData,
boolean applyPhysics) {
CraftBlockData craft = (CraftBlockData) blockData;
World nmsWorld = ((CraftWorld) chunk.getWorld()).getHandle();
Chunk nmsChunk = nmsWorld.d(chunk.getX(), chunk.getZ());
BlockPosition bp = new BlockPosition((chunk.getX() << 4) + x, y, (chunk.getZ() << 4) + z);
// Setting the block to air before setting to another state prevents some console errors
nmsChunk.a(bp, PasteHandlerImpl.AIR, applyPhysics);
nmsChunk.a(bp, craft.getState(), applyPhysics);
}
}

View File

@ -0,0 +1,140 @@
package world.bentobox.bentobox.panels.customizable;
import java.io.File;
import org.bukkit.event.inventory.ClickType;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.TemplatedPanel;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
import world.bentobox.bentobox.api.user.User;
/**
* @author tastybento
*/
public abstract class AbstractPanel {
// ---------------------------------------------------------------------
// Section: Constants
// ---------------------------------------------------------------------
/**
* This constant is used for button to indicate that it is Language type.
*/
public static final String LOCALE = "LOCALE";
/**
* This constant is used for button to indicate that it is previous page type.
*/
public static final String PREVIOUS = "PREVIOUS";
/**
* This constant is used for button to indicate that it is next page type.
*/
public static final String NEXT = "NEXT";
/**
* This constant is used for indicating that pages should contain numbering.
*/
public static final String INDEXING = "indexing";
/**
* This constant stores value for SELECT action that is used in panels.
*/
public static final String SELECT_ACTION = "SELECT";
/**
* This constant stores value for COMMANDS action that is used in panels.
*/
public static final String COMMANDS_ACTION = "COMMANDS";
/**
* This constant stores value for AUTHORS label that is used in panels.
*/
public static final String AUTHORS = "[authors]";
/**
* This constant stores value for SELECTED label that is used in panels.
*/
public static final String SELECTED = "[selected]";
/**
* This variable allows to access plugin object.
*/
final BentoBox plugin;
/**
* This variable stores main command that was triggered.
*/
final CompositeCommand command;
/**
* This variable holds user who opens panel. Without it panel cannot be opened.
*/
final User user;
/**
* This variable holds world where panel is opened. Without it panel cannot be opened.
*/
String mainLabel;
/**
* This variable holds current pageIndex for multi-page island choosing.
*/
int pageIndex;
public AbstractPanel(CompositeCommand command, User user) {
plugin = command.getPlugin();
this.command = command;
this.user = user;
this.pageIndex = 0; // Start with the first page by default
}
// Abstract methods for creating next and previous buttons
protected abstract PanelItem createNextButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot);
protected abstract PanelItem createPreviousButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot);
// Abstract build method to allow each panel to define its own layout
protected abstract void build();
// Default method for pagination, can be overridden by subclasses if needed
protected boolean hasNextPage(int elementListSize, int itemsPerPage) {
return (pageIndex + 1) * itemsPerPage < elementListSize;
}
protected boolean hasPreviousPage() {
return pageIndex > 0;
}
// Method to handle the click event on next/previous buttons
protected boolean handlePageChange(ItemTemplateRecord.ActionRecords action, ClickType clickType,
String actionType) {
if ((clickType == action.clickType() || action.clickType() == ClickType.UNKNOWN)
&& actionType.equalsIgnoreCase(action.actionType())) {
if (actionType.equalsIgnoreCase("NEXT")) {
this.pageIndex++;
} else if (actionType.equalsIgnoreCase("PREVIOUS")) {
this.pageIndex--;
}
build();
return true;
}
return false;
}
/**
* This method returns if panel with the requested name is located in GameModeAddon folder.
* @param addon GameModeAddon that need to be checked.
* @param name Name of the panel.
* @return {@code true} if panel exists, {@code false} otherwise.
*/
protected boolean doesCustomPanelExists(GameModeAddon addon, String name) {
return addon.getDataFolder().exists() && new File(addon.getDataFolder(), "panels").exists()
&& new File(addon.getDataFolder(), "panels" + File.separator + name + ".yml").exists();
}
}

View File

@ -22,8 +22,6 @@ import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.PanelItem;
@ -41,7 +39,7 @@ import world.bentobox.bentobox.util.Util;
* If file with such name is located at gamemode panels directory, then that file will be used. * If file with such name is located at gamemode panels directory, then that file will be used.
* Otherwise, file in BentoBox/panels is used. * Otherwise, file in BentoBox/panels is used.
*/ */
public class IslandCreationPanel public class IslandCreationPanel extends AbstractPanel
{ {
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Section: Constants // Section: Constants
@ -51,37 +49,10 @@ public class IslandCreationPanel
* This constant is used for button to indicate that it is Blueprint Bundle type. * This constant is used for button to indicate that it is Blueprint Bundle type.
*/ */
private static final String BUNDLES = "BUNDLE"; private static final String BUNDLES = "BUNDLE";
/**
* This constant is used for button to indicate that it is previous page type.
*/
private static final String PREVIOUS = "PREVIOUS";
/**
* This constant is used for button to indicate that it is next page type.
*/
private static final String NEXT = "NEXT";
/**
* This constant is used for indicating that pages should contain numbering.
*/
private static final String INDEXING = "indexing";
/**
* This constant stores value for SELECT action that is used in panels.
*/
private static final String SELECT_ACTION = "SELECT";
/**
* This constant stores value for COMMAND action that is used in panels.
*/
private static final String COMMANDS_ACTION = "COMMANDS";
/** /**
* This constant stores value for ERROR message that will be displayed upon failing to run creation commands. * This constant stores value for ERROR message that will be displayed upon failing to run creation commands.
*/ */
private static final String ISLAND_CREATION_COMMANDS = "ISLAND_CREATION_COMMANDS"; private static final String ISLAND_CREATION_COMMANDS = "ISLAND_CREATION_COMMANDS";
/** /**
* Button reference * Button reference
*/ */
@ -91,35 +62,11 @@ public class IslandCreationPanel
// Section: Variables // Section: Variables
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/**
* This variable allows to access plugin object.
*/
private final BentoBox plugin;
/**
* This variable stores main command that was triggered.
*/
private final CompositeCommand mainCommand;
/**
* This variable holds user who opens panel. Without it panel cannot be opened.
*/
private final User user;
/**
* This variable holds world where panel is opened. Without it panel cannot be opened.
*/
private final String mainLabel;
/** /**
* This variable stores filtered elements. * This variable stores filtered elements.
*/ */
private final List<BlueprintBundle> elementList; private final List<BlueprintBundle> elementList;
/**
* This variable holds current pageIndex for multi-page island choosing.
*/
private int pageIndex;
/** /**
* The world that this command applies to * The world that this command applies to
@ -147,8 +94,7 @@ public class IslandCreationPanel
private IslandCreationPanel(@NonNull CompositeCommand command, private IslandCreationPanel(@NonNull CompositeCommand command,
@NonNull User user, @NonNull String label, boolean reset) @NonNull User user, @NonNull String label, boolean reset)
{ {
this.plugin = BentoBox.getInstance(); super(command, user);
this.user = user;
this.mainLabel = label; this.mainLabel = label;
this.world = command.getWorld(); this.world = command.getWorld();
this.reset = reset; this.reset = reset;
@ -159,7 +105,6 @@ public class IslandCreationPanel
.hasPermission(command.getPermissionPrefix() + "island.create." + bundle.getUniqueId())) .hasPermission(command.getPermissionPrefix() + "island.create." + bundle.getUniqueId()))
.toList(); .toList();
this.mainCommand = command;
} }
@ -172,7 +117,8 @@ public class IslandCreationPanel
* Build method manages current panel opening. It uses BentoBox PanelAPI that is easy to use and users can get nice * Build method manages current panel opening. It uses BentoBox PanelAPI that is easy to use and users can get nice
* panels. * panels.
*/ */
private void build() @Override
protected void build()
{ {
// Do not open gui if there is no magic sticks. // Do not open gui if there is no magic sticks.
if (this.elementList.isEmpty()) if (this.elementList.isEmpty())
@ -187,10 +133,10 @@ public class IslandCreationPanel
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
// Set main template. // Set main template.
if (this.doesCustomPanelExists(this.mainCommand.getAddon(), "island_creation_panel")) if (this.doesCustomPanelExists(this.command.getAddon(), "island_creation_panel"))
{ {
// Addon has its own island creation panel. Use it. // Addon has its own island creation panel. Use it.
panelBuilder.template("island_creation_panel", new File(this.mainCommand.getAddon().getDataFolder(), "panels")); panelBuilder.template("island_creation_panel", new File(this.command.getAddon().getDataFolder(), "panels"));
} }
else else
{ {
@ -212,21 +158,6 @@ public class IslandCreationPanel
panelBuilder.build(); panelBuilder.build();
} }
/**
* This method returns if panel with the requested name is located in GameModeAddon folder.
* @param addon GameModeAddon that need to be checked.
* @param name Name of the panel.
* @return {@code true} if panel exists, {@code false} otherwise.
*/
private boolean doesCustomPanelExists(GameModeAddon addon, String name)
{
return addon.getDataFolder().exists() &&
new File(addon.getDataFolder(), "panels").exists()
&& new File(addon.getDataFolder(), "panels" + File.separator + name + ".yml").exists();
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Section: Buttons // Section: Buttons
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -239,8 +170,9 @@ public class IslandCreationPanel
* @param slot the slot * @param slot the slot
* @return the panel item * @return the panel item
*/ */
@Override
@Nullable @Nullable
private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) protected PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{ {
int size = this.elementList.size(); int size = this.elementList.size();
@ -269,12 +201,12 @@ public class IslandCreationPanel
if (template.title() != null) if (template.title() != null)
{ {
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title())); builder.name(this.user.getTranslation(this.command.getWorld(), template.title()));
} }
if (template.description() != null) if (template.description() != null)
{ {
builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(), builder.description(this.user.getTranslation(this.command.getWorld(), template.description(),
TextVariables.NUMBER, String.valueOf(nextPageIndex))); TextVariables.NUMBER, String.valueOf(nextPageIndex)));
} }
@ -299,7 +231,7 @@ public class IslandCreationPanel
// Collect tooltips. // Collect tooltips.
List<String> tooltips = template.actions().stream(). List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null) filter(action -> action.tooltip() != null)
.map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())) .map(action -> this.user.getTranslation(this.command.getWorld(), action.tooltip()))
.filter(text -> !text.isBlank()) .filter(text -> !text.isBlank())
.collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
@ -323,7 +255,8 @@ public class IslandCreationPanel
* @return the panel item * @return the panel item
*/ */
@Nullable @Nullable
private PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) @Override
protected PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{ {
if (this.pageIndex == 0) if (this.pageIndex == 0)
{ {
@ -349,12 +282,12 @@ public class IslandCreationPanel
if (template.title() != null) if (template.title() != null)
{ {
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title())); builder.name(this.user.getTranslation(this.command.getWorld(), template.title()));
} }
if (template.description() != null) if (template.description() != null)
{ {
builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(), builder.description(this.user.getTranslation(this.command.getWorld(), template.description(),
TextVariables.NUMBER, String.valueOf(previousPageIndex))); TextVariables.NUMBER, String.valueOf(previousPageIndex)));
} }
@ -379,7 +312,7 @@ public class IslandCreationPanel
// Collect tooltips. // Collect tooltips.
List<String> tooltips = template.actions().stream(). List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null) filter(action -> action.tooltip() != null)
.map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())) .map(action -> this.user.getTranslation(this.command.getWorld(), action.tooltip()))
.filter(text -> !text.isBlank()) .filter(text -> !text.isBlank())
.collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
@ -467,7 +400,7 @@ public class IslandCreationPanel
if (template.title() != null) if (template.title() != null)
{ {
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title(), builder.name(this.user.getTranslation(this.command.getWorld(), template.title(),
TextVariables.NAME, bundle.getDisplayName())); TextVariables.NAME, bundle.getDisplayName()));
} }
else else
@ -478,7 +411,7 @@ public class IslandCreationPanel
if (template.description() != null) if (template.description() != null)
{ {
builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(), builder.description(this.user.getTranslation(this.command.getWorld(), template.description(),
TextVariables.DESCRIPTION, String.join("\n", bundle.getDescription()))); TextVariables.DESCRIPTION, String.join("\n", bundle.getDescription())));
} }
else else
@ -524,13 +457,13 @@ public class IslandCreationPanel
{ {
if (SELECT_ACTION.equalsIgnoreCase(action.actionType())) { if (SELECT_ACTION.equalsIgnoreCase(action.actionType())) {
user.closeInventory(); user.closeInventory();
this.mainCommand.execute(user, this.mainLabel, this.command.execute(user, this.mainLabel,
Collections.singletonList(bundle.getUniqueId())); Collections.singletonList(bundle.getUniqueId()));
} else if (COMMANDS_ACTION.equalsIgnoreCase(action.actionType())) { } else if (COMMANDS_ACTION.equalsIgnoreCase(action.actionType())) {
Util.runCommands(user, Util.runCommands(user,
Arrays.stream(action.content() Arrays.stream(action.content()
.replaceAll(Pattern.quote(TextVariables.LABEL), .replaceAll(Pattern.quote(TextVariables.LABEL),
this.mainCommand.getTopLabel()) this.command.getTopLabel())
.split("\n")).toList(), .split("\n")).toList(),
ISLAND_CREATION_COMMANDS); ISLAND_CREATION_COMMANDS);
} }
@ -543,7 +476,7 @@ public class IslandCreationPanel
// Collect tooltips. // Collect tooltips.
List<String> tooltips = actions.stream().filter(action -> action.tooltip() != null) List<String> tooltips = actions.stream().filter(action -> action.tooltip() != null)
.map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())) .map(action -> this.user.getTranslation(this.command.getWorld(), action.tooltip()))
.filter(text -> !text.isBlank()) .filter(text -> !text.isBlank())
.collect(Collectors.toCollection(() -> new ArrayList<>(actions.size()))); .collect(Collectors.toCollection(() -> new ArrayList<>(actions.size())));
@ -557,12 +490,10 @@ public class IslandCreationPanel
return builder.build(); return builder.build();
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Section: Static methods // Section: Static methods
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/** /**
* This method is used to open Panel outside this class. It will be much easier to open panel with single method * This method is used to open Panel outside this class. It will be much easier to open panel with single method
* call then initializing new object. * call then initializing new object.
@ -572,11 +503,9 @@ public class IslandCreationPanel
* @param user User who opens panel * @param user User who opens panel
* @param reset true if this is an island reset * @param reset true if this is an island reset
*/ */
public static void openPanel(@NonNull CompositeCommand command, public static void openPanel(@NonNull CompositeCommand command, @NonNull User user, @NonNull String label,
@NonNull User user, @NonNull String label, boolean reset) boolean reset) {
{
new IslandCreationPanel(command, user, label, reset).build(); new IslandCreationPanel(command, user, label, reset).build();
} }
} }

View File

@ -0,0 +1,419 @@
package world.bentobox.bentobox.panels.customizable;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.island.IslandGoCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.panels.PanelItem;
import world.bentobox.bentobox.api.panels.TemplatedPanel;
import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder;
import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder;
import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
/**
* Panel for island homes command
*/
public class IslandHomesPanel extends AbstractPanel
{
private static final String ISLAND = "ISLAND";
/**
* This variable stores filtered elements.
*/
private final Map<String, IslandInfo> islandMap;
private final Map<Integer, String> order = new HashMap<>();
/**
* The world that this command applies to
*/
private final World world;
private final IslandGoCommand goCommand;
// ---------------------------------------------------------------------
// Section: Constructor
// ---------------------------------------------------------------------
/**
* This is internal constructor. It is used internally in current class to avoid creating objects everywhere.
*
* @param command CompositeCommand
* @param user User who opens panel
* @param islandMap map of island names and IslandInfo
*/
private IslandHomesPanel(@NonNull CompositeCommand command, @NonNull User user)
{
super(command, user);
this.world = command.getWorld();
this.islandMap = this.getNameIslandMap(user);
int index = 0;
for (String name : islandMap.keySet()) {
order.put(index++, name);
}
goCommand = (IslandGoCommand) command.getParent().getSubCommand("go").orElse(null);
}
// ---------------------------------------------------------------------
// Section: Methods
// ---------------------------------------------------------------------
/**
* Build method manages current panel opening. It uses BentoBox PanelAPI that is easy to use and users can get nice
* panels.
*/
@Override
protected void build()
{
// Do not open gui if there are no islands
if (this.islandMap.isEmpty())
{
user.sendMessage("general.errors.no-island");
return;
}
// Start building panel.
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
// Set main template.
if (this.doesCustomPanelExists(this.command.getAddon(), "island_homes_panel"))
{
// Addon has its own island homes panel. Use it.
panelBuilder.template("island_homes_panel", new File(this.command.getAddon().getDataFolder(), "panels"));
}
else
{
// Use default island creation panel.
panelBuilder.template("island_homes_panel", new File(this.plugin.getDataFolder(), "panels"));
}
panelBuilder.user(this.user);
panelBuilder.world(world);
// Register button builders
panelBuilder.registerTypeBuilder(ISLAND, this::createIslandButton);
// Register next and previous builders
panelBuilder.registerTypeBuilder(NEXT, this::createNextButton);
panelBuilder.registerTypeBuilder(PREVIOUS, this::createPreviousButton);
// Register unknown type builder.
panelBuilder.build();
}
// ---------------------------------------------------------------------
// Section: Buttons
// ---------------------------------------------------------------------
/**
* Create next button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
@Override
@Nullable
protected PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
int size = this.islandMap.size();
if (size <= slot.amountMap().getOrDefault(ISLAND, 1)
|| 1.0 * size / slot.amountMap().getOrDefault(ISLAND, 1) <= this.pageIndex + 1)
{
// There are no next elements
return null;
}
int nextPageIndex = this.pageIndex + 2;
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
ItemStack clone = template.icon().clone();
if ((boolean) template.dataMap().getOrDefault(INDEXING, false))
{
clone.setAmount(nextPageIndex);
}
builder.icon(clone);
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.command.getWorld(), template.title()));
}
if (template.description() != null)
{
builder.description(this.user.getTranslation(this.command.getWorld(), template.description(),
TextVariables.NUMBER, String.valueOf(nextPageIndex)));
}
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
template.actions().forEach(action -> {
if ((clickType == action.clickType() ||
action.clickType() == ClickType.UNKNOWN) && NEXT.equalsIgnoreCase(action.actionType()))
{
// Next button ignores click type currently.
this.pageIndex++;
this.build();
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null)
.map(action -> this.user.getTranslation(this.command.getWorld(), action.tooltip()))
.filter(text -> !text.isBlank())
.collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
/**
* Create previous button panel item.
*
* @param template the template
* @param slot the slot
* @return the panel item
*/
@Nullable
@Override
protected PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
if (this.pageIndex == 0)
{
// There are no next elements
return null;
}
int previousPageIndex = this.pageIndex;
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
ItemStack clone = template.icon().clone();
if ((boolean) template.dataMap().getOrDefault(INDEXING, false))
{
clone.setAmount(previousPageIndex);
}
builder.icon(clone);
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.command.getWorld(), template.title()));
}
if (template.description() != null)
{
builder.description(this.user.getTranslation(this.command.getWorld(), template.description(),
TextVariables.NUMBER, String.valueOf(previousPageIndex)));
}
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) ->
{
template.actions().forEach(action -> {
if ((clickType == action.clickType() ||
action.clickType() == ClickType.UNKNOWN) && PREVIOUS.equalsIgnoreCase(action.actionType()))
{
// Next button ignores click type currently.
this.pageIndex--;
this.build();
}
});
// Always return true.
return true;
});
// Collect tooltips.
List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null)
.map(action -> this.user.getTranslation(this.command.getWorld(), action.tooltip()))
.filter(text -> !text.isBlank())
.collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips.
if (!tooltips.isEmpty())
{
// Empty line and tooltips.
builder.description("");
builder.description(tooltips);
}
return builder.build();
}
/**
* This method creates and returns island button.
*
* @return PanelItem that represents island button.
*/
@Nullable
private PanelItem createIslandButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
if (this.islandMap.isEmpty())
{
// Does not contain any islands.
return null;
}
int index = this.pageIndex * slot.amountMap().getOrDefault(ISLAND, 1) + slot.slot();
if (index >= this.islandMap.size())
{
// Out of index.
return null;
}
return this.createIslandButtonDetail(template, slot);
}
/**
* This method creates bundle button.
*
* @return PanelItem that allows to select bundle button
*/
private PanelItem createIslandButtonDetail(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{
// Get settings for island.
PanelItemBuilder builder = new PanelItemBuilder();
if (template.icon() != null)
{
builder.icon(template.icon().clone());
}
else
{
builder.icon(Material.GRASS_BLOCK);
}
if (template.title() != null)
{
builder.name(this.user.getTranslation(this.command.getWorld(), template.title(),
TextVariables.NAME, order.get(slot.slot())));
}
else
{
builder.name(this.user.getTranslation("panels.island_homes.buttons.name", TextVariables.NAME,
order.get(slot.slot())));
}
// Add ClickHandler
builder.clickHandler((panel, user, clickType, i) -> {
template.actions().forEach(action -> {
if (goCommand != null) {
String name = order.get(slot.slot());
user.closeInventory();
if (goCommand.canExecute(user, "", List.of(name))) {
goCommand.execute(user, "", List.of(name));
}
}
});
// Always return true.
return true;
});
return builder.build();
}
/**
* Record of islands and the name to type
*/
private record IslandInfo(Island island, boolean islandName) {
}
/**
* This is duplicate code from the Go command.
* @param user user
* @return name and island info
*/
private Map<String, IslandInfo> getNameIslandMap(User user) {
Map<String, IslandInfo> islandMap = new HashMap<>();
int index = 0;
for (Island island : command.getIslands().getIslands(command.getWorld(), user.getUniqueId())) {
index++;
if (island.getName() != null && !island.getName().isBlank()) {
// Name has been set
islandMap.put(island.getName(), new IslandInfo(island, true));
} else {
// Name has not been set
String text = user.getTranslation("protection.flags.ENTER_EXIT_MESSAGES.island", TextVariables.NAME,
user.getName(), TextVariables.DISPLAY_NAME, user.getDisplayName()) + " " + index;
islandMap.put(text, new IslandInfo(island, true));
}
// Add homes. Homes do not need an island specified
island.getHomes().keySet().stream().filter(n -> !n.isBlank())
.forEach(n -> islandMap.put(n, new IslandInfo(island, false)));
}
return islandMap;
}
// ---------------------------------------------------------------------
// Section: Static methods
// ---------------------------------------------------------------------
/**
* This method is used to open Panel outside this class. It will be much easier to open panel with single method
* call then initializing new object.
*
* @param command CompositeCommand object
* @param user User who opens panel
*/
public static void openPanel(@NonNull CompositeCommand command, @NonNull User user) {
new IslandHomesPanel(command, user).build();
}
}

View File

@ -1,6 +1,7 @@
// //
// Created by BONNe // Created by BONNe
// Copyright - 2022 // Copyright - 2022
// Updated by tastybento
// //
@ -23,8 +24,6 @@ import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.BentoBoxLocale; import world.bentobox.bentobox.api.localization.BentoBoxLocale;
import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.localization.TextVariables;
@ -42,8 +41,17 @@ import world.bentobox.bentobox.util.Util;
* If file with such name is located at gamemode panels directory, then that file will be used. * If file with such name is located at gamemode panels directory, then that file will be used.
* Otherwise, file in BentoBox/panels is used. * Otherwise, file in BentoBox/panels is used.
*/ */
public class LanguagePanel public class LanguagePanel extends AbstractPanel
{ {
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* This variable stores filtered elements.
*/
private final List<Locale> elementList;
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Section: Constructor // Section: Constructor
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -55,13 +63,24 @@ public class LanguagePanel
* @param command The main addon command. * @param command The main addon command.
* @param user User who opens panel * @param user User who opens panel
*/ */
private LanguagePanel(@NonNull CompositeCommand command, @NonNull User user) public LanguagePanel(CompositeCommand command, User user) {
{ super(command, user);
this.plugin = BentoBox.getInstance(); this.elementList = plugin.getLocalesManager().getAvailableLocales(true);
this.mainCommand = command; }
this.user = user;
this.elementList = BentoBox.getInstance().getLocalesManager().getAvailableLocales(true); // ---------------------------------------------------------------------
// Section: Static methods
// ---------------------------------------------------------------------
/**
* This method is used to open Panel outside this class. It will be much easier to open panel with single method
* call then initializing new object.
*
* @param command The main addon command.
* @param user User who opens panel
*/
public static void openPanel(@NonNull CompositeCommand command, @NonNull User user) {
new LanguagePanel(command, user).build();
} }
@ -74,7 +93,8 @@ public class LanguagePanel
* Build method manages current panel opening. It uses BentoBox PanelAPI that is easy to use and users can get nice * Build method manages current panel opening. It uses BentoBox PanelAPI that is easy to use and users can get nice
* panels. * panels.
*/ */
private void build() @Override
protected void build()
{ {
// Do not open gui if there is no magic sticks. // Do not open gui if there is no magic sticks.
if (this.elementList.isEmpty()) if (this.elementList.isEmpty())
@ -89,10 +109,10 @@ public class LanguagePanel
TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder();
// Set main template. // Set main template.
if (this.doesCustomPanelExists(this.mainCommand.getAddon(), "language_panel")) if (this.doesCustomPanelExists(this.command.getAddon(), "language_panel"))
{ {
// Addon has its own island creation panel. Use it. // Addon has its own island creation panel. Use it.
panelBuilder.template("language_panel", new File(this.mainCommand.getAddon().getDataFolder(), "panels")); panelBuilder.template("language_panel", new File(this.command.getAddon().getDataFolder(), "panels"));
} }
else else
{ {
@ -115,20 +135,6 @@ public class LanguagePanel
} }
/**
* This method returns if panel with the requested name is located in GameModeAddon folder.
* @param addon GameModeAddon that need to be checked.
* @param name Name of the panel.
* @return {@code true} if panel exists, {@code false} otherwise.
*/
private boolean doesCustomPanelExists(GameModeAddon addon, String name)
{
return addon.getDataFolder().exists() &&
new File(addon.getDataFolder(), "panels").exists() &&
new File(addon.getDataFolder(), "panels" + File.separator + name + ".yml").exists();
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Section: Buttons // Section: Buttons
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -141,8 +147,9 @@ public class LanguagePanel
* @param slot the slot * @param slot the slot
* @return the panel item * @return the panel item
*/ */
@Override
@Nullable @Nullable
private PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) protected PanelItem createNextButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{ {
int size = this.elementList.size(); int size = this.elementList.size();
@ -200,10 +207,9 @@ public class LanguagePanel
// Collect tooltips. // Collect tooltips.
List<String> tooltips = template.actions().stream(). List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null). filter(action -> action.tooltip() != null).map(action -> this.user.getTranslation(action.tooltip()))
map(action -> this.user.getTranslation( action.tooltip())). .filter(text -> !text.isBlank())
filter(text -> !text.isBlank()). .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips. // Add tooltips.
if (!tooltips.isEmpty()) if (!tooltips.isEmpty())
@ -224,8 +230,9 @@ public class LanguagePanel
* @param slot the slot * @param slot the slot
* @return the panel item * @return the panel item
*/ */
@Override
@Nullable @Nullable
private PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) protected PanelItem createPreviousButton(@NonNull ItemTemplateRecord template, TemplatedPanel.ItemSlot slot)
{ {
if (this.pageIndex == 0) if (this.pageIndex == 0)
{ {
@ -251,12 +258,12 @@ public class LanguagePanel
if (template.title() != null) if (template.title() != null)
{ {
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title())); builder.name(this.user.getTranslation(this.command.getWorld(), template.title()));
} }
if (template.description() != null) if (template.description() != null)
{ {
builder.description(this.user.getTranslation(this.mainCommand.getWorld(), template.description(), builder.description(this.user.getTranslation(this.command.getWorld(), template.description(),
TextVariables.NUMBER, String.valueOf(previousPageIndex))); TextVariables.NUMBER, String.valueOf(previousPageIndex)));
} }
@ -281,10 +288,10 @@ public class LanguagePanel
// Collect tooltips. // Collect tooltips.
List<String> tooltips = template.actions().stream(). List<String> tooltips = template.actions().stream().
filter(action -> action.tooltip() != null). filter(action -> action.tooltip() != null)
map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())). .map(action -> this.user.getTranslation(this.command.getWorld(), action.tooltip()))
filter(text -> !text.isBlank()). .filter(text -> !text.isBlank())
collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size())));
// Add tooltips. // Add tooltips.
if (!tooltips.isEmpty()) if (!tooltips.isEmpty())
@ -330,9 +337,8 @@ public class LanguagePanel
{ {
// Try to find locale with requested ID. if not found, use already collected locale. // Try to find locale with requested ID. if not found, use already collected locale.
locale = this.elementList.stream(). locale = this.elementList.stream().
filter(localeID -> localeID.toLanguageTag().equals(template.dataMap().get("lang_id"))). filter(localeID -> localeID.toLanguageTag().equals(template.dataMap().get("lang_id"))).findFirst()
findFirst(). .orElse(locale);
orElse(locale);
} }
return this.createLocaleButton(template, locale); return this.createLocaleButton(template, locale);
@ -376,7 +382,7 @@ public class LanguagePanel
if (template.title() != null) if (template.title() != null)
{ {
builder.name(this.user.getTranslation(this.mainCommand.getWorld(), template.title(), builder.name(this.user.getTranslation(this.command.getWorld(), template.title(),
TextVariables.NAME, WordUtils.capitalize(locale.getDisplayName(this.user.getLocale())))); TextVariables.NAME, WordUtils.capitalize(locale.getDisplayName(this.user.getLocale()))));
} }
else else
@ -405,28 +411,25 @@ public class LanguagePanel
if (template.description() != null) if (template.description() != null)
{ {
descriptionText = this.user.getTranslationOrNothing(template.description(), descriptionText = this.user.getTranslationOrNothing(template.description(),
AUTHORS, authors.toString(), AUTHORS, authors.toString(), SELECTED, selected.toString());
SELECTED, selected.toString());
} }
else else
{ {
descriptionText = this.user.getTranslationOrNothing(reference + "description", descriptionText = this.user.getTranslationOrNothing(reference + "description",
AUTHORS, authors.toString(), AUTHORS, authors.toString(), SELECTED, selected.toString());
SELECTED, selected.toString());
} }
descriptionText = descriptionText.replaceAll("(?m)^[ \\t]*\\r?\\n", ""). descriptionText = descriptionText.replaceAll("(?m)^[ \\t]*\\r?\\n", "").
replaceAll("(?<!\\\\)\\|", "\n"). replaceAll("(?<!\\\\)\\|", "\n").replaceAll("\\\\\\|", "|");
replaceAll("\\\\\\|", "|");
builder.description(descriptionText); builder.description(descriptionText);
// Display actions only for non-selected locales. // Display actions only for non-selected locales.
List<ItemTemplateRecord.ActionRecords> actions = template.actions().stream(). List<ItemTemplateRecord.ActionRecords> actions = template.actions().stream().
filter(action -> !this.user.getLocale().equals(locale) && filter(action -> !this.user.getLocale().equals(locale)
(SELECT_ACTION.equalsIgnoreCase(action.actionType()) || && (SELECT_ACTION.equalsIgnoreCase(action.actionType())
COMMANDS_ACTION.equalsIgnoreCase(action.actionType()))). || COMMANDS_ACTION.equalsIgnoreCase(action.actionType())))
toList(); .toList();
// Add ClickHandler // Add ClickHandler
builder.clickHandler((panel, user, clickType, i) -> builder.clickHandler((panel, user, clickType, i) ->
@ -446,9 +449,9 @@ public class LanguagePanel
else if (COMMANDS_ACTION.equalsIgnoreCase(action.actionType())) else if (COMMANDS_ACTION.equalsIgnoreCase(action.actionType()))
{ {
Util.runCommands(user, Util.runCommands(user,
Arrays.stream(action.content(). Arrays.stream(action.content()
replaceAll(Pattern.quote(TextVariables.LABEL), this.mainCommand.getTopLabel()). .replaceAll(Pattern.quote(TextVariables.LABEL), this.command.getTopLabel())
split("\n")). .split("\n")).
toList(), toList(),
"CHANGE_LOCALE_COMMANDS"); "CHANGE_LOCALE_COMMANDS");
} }
@ -461,10 +464,10 @@ public class LanguagePanel
// Collect tooltips. // Collect tooltips.
List<String> tooltips = actions.stream(). List<String> tooltips = actions.stream().
filter(action -> action.tooltip() != null). filter(action -> action.tooltip() != null)
map(action -> this.user.getTranslation(this.mainCommand.getWorld(), action.tooltip())). .map(action -> this.user.getTranslation(this.command.getWorld(), action.tooltip()))
filter(text -> !text.isBlank()). .filter(text -> !text.isBlank())
collect(Collectors.toCollection(() -> new ArrayList<>(actions.size()))); .collect(Collectors.toCollection(() -> new ArrayList<>(actions.size())));
// Add tooltips. // Add tooltips.
if (!tooltips.isEmpty()) if (!tooltips.isEmpty())
@ -478,97 +481,6 @@ public class LanguagePanel
} }
// ---------------------------------------------------------------------
// Section: Static methods
// ---------------------------------------------------------------------
/**
* This method is used to open Panel outside this class. It will be much easier to open panel with single method
* call then initializing new object.
*
* @param command The main addon command.
* @param user User who opens panel
*/
public static void openPanel(@NonNull CompositeCommand command, @NonNull User user)
{
new LanguagePanel(command, user).build();
} }
// ---------------------------------------------------------------------
// Section: Constants
// ---------------------------------------------------------------------
/**
* This constant is used for button to indicate that it is Language type.
*/
private static final String LOCALE = "LOCALE";
/**
* This constant is used for button to indicate that it is previous page type.
*/
private static final String PREVIOUS = "PREVIOUS";
/**
* This constant is used for button to indicate that it is next page type.
*/
private static final String NEXT = "NEXT";
/**
* This constant is used for indicating that pages should contain numbering.
*/
private static final String INDEXING = "indexing";
/**
* This constant stores value for SELECT action that is used in panels.
*/
private static final String SELECT_ACTION = "SELECT";
/**
* This constant stores value for COMMANDS action that is used in panels.
*/
private static final String COMMANDS_ACTION = "COMMANDS";
/**
* This constant stores value for AUTHORS label that is used in panels.
*/
public static final String AUTHORS = "[authors]";
/**
* This constant stores value for SELECTED label that is used in panels.
*/
public static final String SELECTED = "[selected]";
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* This variable allows to access plugin object.
*/
private final BentoBox plugin;
/**
* This variable stores the main command object.
*/
private final CompositeCommand mainCommand;
/**
* This variable holds user who opens panel. Without it panel cannot be opened.
*/
private final User user;
/**
* This variable stores filtered elements.
*/
private final List<Locale> elementList;
/**
* This variable holds current pageIndex for multi-page island choosing.
*/
private int pageIndex;
}

View File

@ -743,6 +743,7 @@ public class Util {
throw new IllegalStateException("Class " + clazz.getName() + " does not implement WorldRegenerator"); throw new IllegalStateException("Class " + clazz.getName() + " does not implement WorldRegenerator");
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
plugin.logWarning("No Regenerator found for " + bukkitVersion + ", falling back to Bukkit API."); plugin.logWarning("No Regenerator found for " + bukkitVersion + ", falling back to Bukkit API.");
handler = new world.bentobox.bentobox.nms.fallback.WorldRegeneratorImpl(); handler = new world.bentobox.bentobox.nms.fallback.WorldRegeneratorImpl();
} }
@ -772,6 +773,7 @@ public class Util {
throw new IllegalStateException("Class " + clazz.getName() + " does not implement PasteHandler"); throw new IllegalStateException("Class " + clazz.getName() + " does not implement PasteHandler");
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace();
plugin.logWarning("No PasteHandler found for " + bukkitVersion + ", falling back to Bukkit API."); plugin.logWarning("No PasteHandler found for " + bukkitVersion + ", falling back to Bukkit API.");
handler = new world.bentobox.bentobox.nms.fallback.PasteHandlerImpl(); handler = new world.bentobox.bentobox.nms.fallback.PasteHandlerImpl();
} }

View File

@ -249,7 +249,12 @@ public class ServerCompatibility {
/** /**
* @since 2.5.0 * @since 2.5.0
*/ */
V1_21_1(Compatibility.COMPATIBLE); V1_21_1(Compatibility.COMPATIBLE),
/**
* @since 2.7.0
*/
V1_21_2(Compatibility.INCOMPATIBLE), V1_21_3(Compatibility.COMPATIBLE);
private final Compatibility compatibility; private final Compatibility compatibility;

View File

@ -96,8 +96,17 @@ commands:
description: purge islands abandoned for more than [days] description: purge islands abandoned for more than [days]
days-one-or-more: Must be at least 1 day or more days-one-or-more: Must be at least 1 day or more
purgable-islands: '&a Found &b [number] &a purgable islands.' purgable-islands: '&a Found &b [number] &a purgable islands.'
too-many: |
&b This is a lot and could take a very long time to delete.
&b Consider using Regionerator plugin for deleting world chunks
&b and setting keep-previous-island-on-reset: true in BentoBox's config.yml.
&b Then run a purge.
purge-in-progress: '&c Purging in progress. Use &b /[label] purge stop &c to purge-in-progress: '&c Purging in progress. Use &b /[label] purge stop &c to
cancel.' cancel.'
scanning: '&a Scanning islands in the database. This may take a while depending on how many you have...'
scanning-in-progress: '&c Scanning in progress, please wait'
none-found: '&c No islands found to purge.'
total-islands: '&a You have [number] islands in your database in all worlds.'
number-error: '&c Argument must be a number of days' number-error: '&c Argument must be a number of days'
confirm: '&d Type &b /[label] purge confirm &d to start purging' confirm: '&d Type &b /[label] purge confirm &d to start purging'
completed: '&a Purging stopped.' completed: '&a Purging stopped.'
@ -1906,6 +1915,12 @@ panel:
# This section contains values for BentoBox panels. # This section contains values for BentoBox panels.
panels: panels:
# The section of translations used in Island Homes Panel
island_homes:
title: "&2&l Your island homes"
buttons:
# This button is used for displaying islands to teleport to
name: "&l [name]"
# The section of translations used in Island Creation Panel # The section of translations used in Island Creation Panel
island_creation: island_creation:
title: "&2&l Pick an island" title: "&2&l Pick an island"

View File

@ -0,0 +1,69 @@
# This is default island homes panel. It is used in all situations when gamemode addon does not have specified their
# of panel.
island_homes_panel:
title: panels.island_homes.title # The title of panel or link to the localization location.
type: INVENTORY # The type of inventory: INVENTORY, DROPPER, HOPPER
background: # The item that will be displayed in empty spots. This section can be removed.
icon: BLACK_STAINED_GLASS_PANE # The icon of background item
title: "&b&r" # Empty text # The text of background item
border: # The item that will be displayed around the inventory. This section can be removed.
icon: BLACK_STAINED_GLASS_PANE # The icon of background item
title: "&b&r" # Empty text # The text of background item
force-shown: [] # Allow to specify (1-6, 1-3, 1) which rows must be showed regardless of empty elements.
content: # Allow to define buttons in your panel.
2:
2: island_button # String values are expected to be `reusables` that are defined at the end of this file.
3: island_button
4: island_button
5: island_button
6: island_button
7: island_button
8: island_button
3:
1:
icon: tipped_arrow{CustomPotionColor:11546150} # The icon for button
title: panels.buttons.previous.name # The name of button, or link to the localization.
description: panels.buttons.previous.description # The description of button, or link to the localization.
data:
type: PREVIOUS # Indicates what button is doing. Available values depends on panel
indexing: true # Parameter for button.
actions: # List of actions that button can do. Available values depends on button
previous:
click-type: UNKNOWN # UNKNOWN means that any click type is respected.
tooltip: panels.tips.click-to-previous # Tooltips are always generated an empty line bellow description/title. Not required.
2: island_button
3: island_button
4: island_button
5: island_button
6: island_button
7: island_button
8: island_button
9:
icon: tipped_arrow{CustomPotionColor:8439583}
title: panels.buttons.next.name
description: panels.buttons.next.description
data:
type: NEXT
indexing: true
actions:
next:
click-type: UNKNOWN
tooltip: panels.tips.click-to-next
4:
2: island_button
3: island_button
4: island_button
5: island_button
6: island_button
7: island_button
8: island_button
reusable: # List of reoccurring buttons in the panels.
island_button: # The ID of the button
# icon: GRASS_BLOCK
title: panels.island_homes.buttons.name
data:
type: ISLAND
actions:
select:
click-type: UNKNOWN
tooltip: panels.tips.click-to-choose

View File

@ -280,7 +280,7 @@ public abstract class AbstractCommonSetup {
List<TextComponent> capturedMessages = captor.getAllValues(); List<TextComponent> capturedMessages = captor.getAllValues();
// Count the number of occurrences of the expectedMessage in the captured messages // Count the number of occurrences of the expectedMessage in the captured messages
long actualOccurrences = capturedMessages.stream().map(component -> component.toPlainText()) // Convert each TextComponent to plain text long actualOccurrences = capturedMessages.stream().map(component -> component.toLegacyText()) // Convert each TextComponent to plain text
.filter(messageText -> messageText.contains(expectedMessage)) // Check if the message contains the expected text .filter(messageText -> messageText.contains(expectedMessage)) // Check if the message contains the expected text
.count(); // Count how many times the expected message appears .count(); // Count how many times the expected message appears
@ -298,13 +298,14 @@ public abstract class AbstractCommonSetup {
*/ */
public EntityExplodeEvent getExplodeEvent(Entity entity, Location l, List<Block> list) { public EntityExplodeEvent getExplodeEvent(Entity entity, Location l, List<Block> list) {
//return new EntityExplodeEvent(entity, l, list, 0, null); //return new EntityExplodeEvent(entity, l, list, 0, null);
return new EntityExplodeEvent(entity, l, list, 0); return new EntityExplodeEvent(entity, l, list, 0, null);
} }
public PlayerDeathEvent getPlayerDeathEvent(Player player, List<ItemStack> drops, int droppedExp, int newExp, public PlayerDeathEvent getPlayerDeathEvent(Player player, List<ItemStack> drops, int droppedExp, int newExp,
int newTotalExp, int newLevel, @Nullable String deathMessage) { int newTotalExp, int newLevel, @Nullable String deathMessage) {
//return new PlayerDeathEvent(player, null, drops, droppedExp, newExp, newTotalExp, newLevel, deathMessage); //Technically this null is not allowed, but it works right now
return new PlayerDeathEvent(player, drops, droppedExp, newExp, newTotalExp, newLevel, deathMessage); return new PlayerDeathEvent(player, null, drops, droppedExp, newExp,
newTotalExp, newLevel, deathMessage);
} }
} }

View File

@ -5,7 +5,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -23,7 +22,6 @@ import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemFactory;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
@ -167,8 +165,7 @@ public class AdminSettingsCommandTest extends RanksManagerBeforeClassTest {
when(itemFactory.getItemMeta(any())).thenReturn(bannerMeta); when(itemFactory.getItemMeta(any())).thenReturn(bannerMeta);
when(Bukkit.getItemFactory()).thenReturn(itemFactory); when(Bukkit.getItemFactory()).thenReturn(itemFactory);
Inventory inventory = mock(Inventory.class); Inventory inventory = mock(Inventory.class);
when(Bukkit.createInventory(eq(null), Mockito.anyInt(), any())).thenReturn(inventory); when(Bukkit.createInventory(any(), Mockito.anyInt(), anyString())).thenReturn(inventory);
when(Bukkit.createInventory(eq(null), any(InventoryType.class), any())).thenReturn(inventory);
// Flags manager // Flags manager
when(Bukkit.getPluginManager()).thenReturn(pluginManager); when(Bukkit.getPluginManager()).thenReturn(pluginManager);
FlagsManager fm = new FlagsManager(plugin); FlagsManager fm = new FlagsManager(plugin);

View File

@ -4,21 +4,28 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.junit.After; import org.junit.After;
@ -35,9 +42,11 @@ import org.powermock.reflect.Whitebox;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.Settings;
import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.events.island.IslandDeletedEvent; import world.bentobox.bentobox.api.events.island.IslandDeletedEvent;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.CommandsManager;
@ -73,12 +82,19 @@ public class AdminPurgeCommandTest {
private PlayersManager pm; private PlayersManager pm;
@Mock @Mock
private @NonNull Location location; private @NonNull Location location;
@Mock
private BukkitScheduler scheduler;
/**
*/
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
// Mock the method to immediately run the Runnable
when(scheduler.runTaskLater(eq(plugin), any(Runnable.class), anyLong())).thenAnswer(invocation -> {
Runnable task = invocation.getArgument(1);
task.run(); // Immediately run the Runnable
return null; // or return a mock of the Task if needed
});
when(Bukkit.getScheduler()).thenReturn(scheduler);
// Set up plugin // Set up plugin
Whitebox.setInternalState(BentoBox.class, "instance", plugin); Whitebox.setInternalState(BentoBox.class, "instance", plugin);
@ -95,6 +111,7 @@ public class AdminPurgeCommandTest {
when(plugin.getIslands()).thenReturn(im); when(plugin.getIslands()).thenReturn(im);
// No islands by default // No islands by default
when(im.getIslands()).thenReturn(Collections.emptyList()); when(im.getIslands()).thenReturn(Collections.emptyList());
when(im.getIslandsASync()).thenReturn(CompletableFuture.completedFuture(Collections.emptyList()));
// IWM // IWM
IslandWorldManager iwm = mock(IslandWorldManager.class); IslandWorldManager iwm = mock(IslandWorldManager.class);
@ -110,6 +127,10 @@ public class AdminPurgeCommandTest {
when(plugin.getPlayers()).thenReturn(pm); when(plugin.getPlayers()).thenReturn(pm);
when(pm.getName(any())).thenReturn("name"); when(pm.getName(any())).thenReturn("name");
Settings settings = new Settings();
// Settings
when(plugin.getSettings()).thenReturn(settings);
// Command // Command
apc = new AdminPurgeCommand(ac); apc = new AdminPurgeCommand(ac);
} }
@ -286,13 +307,13 @@ public class AdminPurgeCommandTest {
when(island.getOwner()).thenReturn(UUID.randomUUID()); when(island.getOwner()).thenReturn(UUID.randomUUID());
when(island.isOwned()).thenReturn(true); when(island.isOwned()).thenReturn(true);
when(island.getMemberSet()).thenReturn(ImmutableSet.of(UUID.randomUUID())); when(island.getMemberSet()).thenReturn(ImmutableSet.of(UUID.randomUUID()));
when(im.getIslands()).thenReturn(Collections.singleton(island)); when(im.getIslandsASync()).thenReturn(CompletableFuture.completedFuture(List.of(island)));
OfflinePlayer op = mock(OfflinePlayer.class); when(pm.getLastLoginTimestamp(any())).thenReturn(962434800L);
when(op.getLastPlayed()).thenReturn(0L); assertTrue(apc.execute(user, "", Collections.singletonList("10"))); // 10 days ago
when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(op); verify(user).sendMessage("commands.admin.purge.scanning");
assertFalse(apc.execute(user, "", Collections.singletonList("10"))); verify(user).sendMessage("commands.admin.purge.total-islands", "[number]", "1");
verify(user).sendMessage(eq("commands.admin.purge.purgable-islands"), eq("[number]"), eq("1")); verify(user, never()).sendMessage("commands.admin.purge.none-found");
verify(user).sendMessage(eq("commands.admin.purge.confirm"), eq("[label]"), eq("bsb")); verify(user).sendMessage("commands.admin.purge.confirm", TextVariables.LABEL, "bsb");
} }
@ -307,7 +328,7 @@ public class AdminPurgeCommandTest {
testExecuteUserStringListOfStringIslandsFound(); testExecuteUserStringListOfStringIslandsFound();
assertTrue(apc.execute(user, "", Collections.singletonList("confirm"))); assertTrue(apc.execute(user, "", Collections.singletonList("confirm")));
verify(im).deleteIsland(eq(island), eq(true), eq(null)); verify(im).deleteIsland(eq(island), eq(true), eq(null));
verify(plugin, times(4)).log(any()); verify(plugin).log(any());
verify(user).sendMessage(eq("commands.admin.purge.see-console-for-status"), eq("[label]"), eq("bsb")); verify(user).sendMessage(eq("commands.admin.purge.see-console-for-status"), eq("[label]"), eq("bsb"));
} }
@ -367,13 +388,26 @@ public class AdminPurgeCommandTest {
/** /**
* Test method for {@link world.bentobox.bentobox.api.commands.admin.purge.AdminPurgeCommand#getOldIslands(int)} * Test method for {@link world.bentobox.bentobox.api.commands.admin.purge.AdminPurgeCommand#getOldIslands(int)}
* @throws TimeoutException
* @throws ExecutionException
* @throws InterruptedException
*/ */
@Test @Test
public void testGetOldIslands() { public void testGetOldIslands() throws InterruptedException, ExecutionException, TimeoutException {
assertTrue(apc.getOldIslands(10).isEmpty()); assertTrue(apc.execute(user, "", Collections.singletonList("10"))); // 10 days ago
// First, ensure that the result is empty
CompletableFuture<Set<String>> result = apc.getOldIslands(10);
Set<String> set = result.join();
assertTrue(set.isEmpty());
// Mocking Islands and their retrieval
Island island1 = mock(Island.class);
Island island2 = mock(Island.class); Island island2 = mock(Island.class);
when(im.getIslands()).thenReturn(Set.of(island, island2));
assertTrue(apc.getOldIslands(10).isEmpty()); when(im.getIslandsASync()).thenReturn(CompletableFuture.completedFuture(List.of(island1, island2)));
// Now, check again after mocking islands
CompletableFuture<Set<String>> futureWithIslands = apc.getOldIslands(10);
assertTrue(futureWithIslands.get(5, TimeUnit.SECONDS).isEmpty()); // Adjust this assertion based on the expected behavior of getOldIslands
} }
} }

View File

@ -8,7 +8,6 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -39,7 +38,6 @@ import org.powermock.reflect.Whitebox;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.Settings; import world.bentobox.bentobox.Settings;
import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.CommandsManager; import world.bentobox.bentobox.managers.CommandsManager;
@ -200,12 +198,8 @@ public class IslandHomesCommandTest {
*/ */
@Test @Test
public void testExecuteUserStringListOfString() { public void testExecuteUserStringListOfString() {
when(im.getIslands(world, user)).thenReturn(List.of(island));
IslandHomesCommand isc = new IslandHomesCommand(ic); IslandHomesCommand isc = new IslandHomesCommand(ic);
assertTrue(isc.canExecute(user, "island", Collections.emptyList()));
assertTrue(isc.execute(user, "island", Collections.emptyList())); assertTrue(isc.execute(user, "island", Collections.emptyList()));
verify(user).sendMessage("commands.island.sethome.homes-are");
verify(user, times(4)).sendMessage(eq("commands.island.sethome.home-list-syntax"), eq(TextVariables.NAME), anyString());
} }
} }

View File

@ -18,7 +18,6 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemFactory; import org.bukkit.inventory.ItemFactory;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
@ -170,7 +169,6 @@ public class IslandTeamInviteCommandTest extends RanksManagerBeforeClassTest {
when(Bukkit.getItemFactory()).thenReturn(itemFactory); when(Bukkit.getItemFactory()).thenReturn(itemFactory);
Inventory inventory = mock(Inventory.class); Inventory inventory = mock(Inventory.class);
when(Bukkit.createInventory(eq(null), anyInt(), any())).thenReturn(inventory); when(Bukkit.createInventory(eq(null), anyInt(), any())).thenReturn(inventory);
when(Bukkit.createInventory(eq(null), any(InventoryType.class), any())).thenReturn(inventory);
// Command under test // Command under test
itl = new IslandTeamInviteCommand(ic); itl = new IslandTeamInviteCommand(ic);

View File

@ -7,6 +7,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -29,13 +30,15 @@ import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.util.ItemParser;
/** /**
* Tests BentoBoxLocale class * Tests BentoBoxLocale class
* @author tastybento * @author tastybento
* *
*/ */
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest( { Bukkit.class }) @PrepareForTest({ Bukkit.class, ItemParser.class })
public class BentoBoxLocaleTest { public class BentoBoxLocaleTest {
private BentoBoxLocale localeObject; private BentoBoxLocale localeObject;
@ -45,6 +48,8 @@ public class BentoBoxLocaleTest {
*/ */
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
PowerMockito.mockStatic(ItemParser.class, Mockito.RETURNS_MOCKS);
when(ItemParser.parse(anyString())).thenReturn(new ItemStack(Material.WHITE_BANNER));
PowerMockito.mockStatic(Bukkit.class); PowerMockito.mockStatic(Bukkit.class);
// Mock item factory (for itemstacks) // Mock item factory (for itemstacks)
ItemFactory itemFactory = mock(ItemFactory.class); ItemFactory itemFactory = mock(ItemFactory.class);
@ -112,8 +117,6 @@ public class BentoBoxLocaleTest {
public void testGetBanner() { public void testGetBanner() {
ItemStack banner = localeObject.getBanner(); ItemStack banner = localeObject.getBanner();
assertEquals(Material.WHITE_BANNER, banner.getType()); assertEquals(Material.WHITE_BANNER, banner.getType());
// Check that three patters were added
Mockito.verify(bannerMeta, Mockito.times(3)).addPattern(Mockito.any());
} }
/** /**

View File

@ -1,16 +1,11 @@
/**
*
*/
package world.bentobox.bentobox.api.panels.builders; package world.bentobox.bentobox.api.panels.builders;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.inventory.Inventory;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -35,8 +30,6 @@ import world.bentobox.bentobox.api.user.User;
@PrepareForTest({Bukkit.class}) @PrepareForTest({Bukkit.class})
public class PanelBuilderTest { public class PanelBuilderTest {
/**
*/
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS); PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
@ -44,9 +37,6 @@ public class PanelBuilderTest {
BentoBox plugin = mock(BentoBox.class); BentoBox plugin = mock(BentoBox.class);
Whitebox.setInternalState(BentoBox.class, "instance", plugin); Whitebox.setInternalState(BentoBox.class, "instance", plugin);
Inventory inv = mock(Inventory.class);
when(Bukkit.createInventory(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(inv);
} }
@After @After

View File

@ -8,6 +8,8 @@ import java.util.Map;
import org.bukkit.DyeColor; import org.bukkit.DyeColor;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.entity.ChestedHorse; import org.bukkit.entity.ChestedHorse;
import org.bukkit.entity.Cow; import org.bukkit.entity.Cow;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
@ -21,6 +23,7 @@ import org.bukkit.inventory.ItemStack;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
@ -34,6 +37,7 @@ import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity.MythicMobR
* *
*/ */
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@Ignore("Cannot mock Villager Professions anynore")
public class BlueprintEntityTest { public class BlueprintEntityTest {
@Mock @Mock
@ -55,7 +59,8 @@ public class BlueprintEntityTest {
*/ */
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
when(villager.getProfession()).thenReturn(Profession.LIBRARIAN); when(villager.getProfession())
.thenReturn(Registry.VILLAGER_PROFESSION.get(NamespacedKey.minecraft("librarian")));
when(villager.getVillagerExperience()).thenReturn(100); when(villager.getVillagerExperience()).thenReturn(100);
when(villager.getVillagerLevel()).thenReturn(2); when(villager.getVillagerLevel()).thenReturn(2);
when(villager.getVillagerType()).thenReturn(Villager.Type.PLAINS); when(villager.getVillagerType()).thenReturn(Villager.Type.PLAINS);

View File

@ -10,7 +10,6 @@ import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.damage.DamageSource;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent;
import org.junit.After; import org.junit.After;
@ -41,7 +40,6 @@ public class DeathListenerTest extends AbstractCommonSetup {
private World world; private World world;
private UUID uuid; private UUID uuid;
private IslandWorldManager iwm; private IslandWorldManager iwm;
private DamageSource ds = null;
@Before @Before
public void setUp() { public void setUp() {

View File

@ -126,7 +126,7 @@ public class PanelListenerManagerTest {
PanelListenerManager.getOpenPanels().clear(); PanelListenerManager.getOpenPanels().clear();
} }
class MyView extends InventoryView { class MyView implements InventoryView {
private final Inventory top; private final Inventory top;
private final String name; private final String name;
@ -195,6 +195,53 @@ public class PanelListenerManagerTest {
return null; return null;
} }
@Override
public void setCursor(ItemStack item) {
// TODO Auto-generated method stub
}
@Override
public ItemStack getCursor() {
// TODO Auto-generated method stub
return null;
}
@Override
public Inventory getInventory(int rawSlot) {
// TODO Auto-generated method stub
return null;
}
@Override
public int convertSlot(int rawSlot) {
// TODO Auto-generated method stub
return 0;
}
@Override
public SlotType getSlotType(int slot) {
// TODO Auto-generated method stub
return null;
}
@Override
public void close() {
// TODO Auto-generated method stub
}
@Override
public int countSlots() {
// TODO Auto-generated method stub
return 0;
}
@Override
public boolean setProperty(Property prop, int value) {
// TODO Auto-generated method stub
return false;
}
} }

View File

@ -338,7 +338,7 @@ public class BreakBlocksListenerTest extends AbstractCommonSetup {
when(island.isAllowed(any(), any())).thenReturn(false); when(island.isAllowed(any(), any())).thenReturn(false);
Vehicle vehicle = mock(Vehicle.class); Vehicle vehicle = mock(Vehicle.class);
when(vehicle.getLocation()).thenReturn(location); when(vehicle.getLocation()).thenReturn(location);
when(vehicle.getType()).thenReturn(EntityType.BOAT); when(vehicle.getType()).thenReturn(EntityType.OAK_BOAT);
VehicleDamageEvent e = new VehicleDamageEvent(vehicle, mockPlayer, 10); VehicleDamageEvent e = new VehicleDamageEvent(vehicle, mockPlayer, 10);
bbl.onVehicleDamageEvent(e); bbl.onVehicleDamageEvent(e);
assertTrue(e.isCancelled()); assertTrue(e.isCancelled());

View File

@ -18,6 +18,7 @@ import java.util.Optional;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
import org.bukkit.entity.Arrow; import org.bukkit.entity.Arrow;
@ -35,6 +36,7 @@ import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -45,7 +47,9 @@ import org.powermock.modules.junit4.PowerMockRunner;
import world.bentobox.bentobox.AbstractCommonSetup; import world.bentobox.bentobox.AbstractCommonSetup;
import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.configuration.WorldSettings;
import world.bentobox.bentobox.lists.Flags; import world.bentobox.bentobox.lists.Flags;
import world.bentobox.bentobox.managers.IslandWorldManager;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@ -56,6 +60,8 @@ public class TNTListenerTest extends AbstractCommonSetup {
private Block block; private Block block;
@Mock @Mock
private Entity entity; private Entity entity;
@Mock
private IslandWorldManager iwm;
// Class under test // Class under test
private ExplosionListener listener; private ExplosionListener listener;
@ -65,6 +71,18 @@ public class TNTListenerTest extends AbstractCommonSetup {
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
// IWM - for some reason, this doesn't work in the AbstractCommonSetup
when(plugin.getIWM()).thenReturn(iwm);
when(iwm.inWorld(any(Location.class))).thenReturn(true);
when(iwm.inWorld(any(World.class))).thenReturn(true);
when(iwm.getFriendlyName(any())).thenReturn("BSkyBlock");
// Addon
when(iwm.getAddon(any())).thenReturn(Optional.empty());
@Nullable
WorldSettings worldSet = new TestWorldSettings();
when(iwm.getWorldSettings(any())).thenReturn(worldSet);
// Monsters and animals // Monsters and animals
Zombie zombie = mock(Zombie.class); Zombie zombie = mock(Zombie.class);
when(zombie.getLocation()).thenReturn(location); when(zombie.getLocation()).thenReturn(location);

View File

@ -65,7 +65,7 @@ public class CreeperListenerTest extends AbstractCommonSetup {
Entity entity = mock(Entity.class); Entity entity = mock(Entity.class);
when(entity.getType()).thenReturn(EntityType.TNT); when(entity.getType()).thenReturn(EntityType.TNT);
when(iwm.inWorld(location)).thenReturn(true); when(iwm.inWorld(location)).thenReturn(true);
EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0); EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0, null);
cl.onExplosion(event); cl.onExplosion(event);
assertFalse(event.isCancelled()); assertFalse(event.isCancelled());
} }
@ -80,7 +80,7 @@ public class CreeperListenerTest extends AbstractCommonSetup {
when(entity.getLocation()).thenReturn(location); when(entity.getLocation()).thenReturn(location);
when(entity.getType()).thenReturn(EntityType.CREEPER); when(entity.getType()).thenReturn(EntityType.CREEPER);
when(iwm.inWorld(location)).thenReturn(false); when(iwm.inWorld(location)).thenReturn(false);
EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0); EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0, null);
cl.onExplosion(event); cl.onExplosion(event);
assertFalse(event.isCancelled()); assertFalse(event.isCancelled());
} }
@ -98,7 +98,7 @@ public class CreeperListenerTest extends AbstractCommonSetup {
when(entity.getLocation()).thenReturn(location); when(entity.getLocation()).thenReturn(location);
when(entity.getType()).thenReturn(EntityType.CREEPER); when(entity.getType()).thenReturn(EntityType.CREEPER);
when(iwm.inWorld(location)).thenReturn(true); when(iwm.inWorld(location)).thenReturn(true);
EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0); EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0, null);
cl.onExplosion(event); cl.onExplosion(event);
assertFalse(event.isCancelled()); assertFalse(event.isCancelled());
assertFalse(event.blockList().isEmpty()); // No clearing of block list assertFalse(event.blockList().isEmpty()); // No clearing of block list
@ -119,7 +119,7 @@ public class CreeperListenerTest extends AbstractCommonSetup {
when(entity.getLocation()).thenReturn(location); when(entity.getLocation()).thenReturn(location);
when(entity.getType()).thenReturn(EntityType.CREEPER); when(entity.getType()).thenReturn(EntityType.CREEPER);
when(iwm.inWorld(location)).thenReturn(true); when(iwm.inWorld(location)).thenReturn(true);
EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0); EntityExplodeEvent event = new EntityExplodeEvent(entity, location, list, 0, null);
cl.onExplosion(event); cl.onExplosion(event);
assertFalse(event.isCancelled()); assertFalse(event.isCancelled());
assertTrue(event.blockList().isEmpty()); // No clearing of block list assertTrue(event.blockList().isEmpty()); // No clearing of block list

View File

@ -58,6 +58,7 @@ import org.bukkit.scheduler.BukkitScheduler;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
@ -91,7 +92,7 @@ import world.bentobox.bentobox.managers.island.IslandCache;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
@RunWith(PowerMockRunner.class) @RunWith(PowerMockRunner.class)
@PrepareForTest({ Bukkit.class, BentoBox.class, Util.class, Location.class, MultiLib.class, DatabaseSetup.class, }) @PrepareForTest({ Bukkit.class, BentoBox.class, Util.class, Location.class, MultiLib.class, DatabaseSetup.class })
public class IslandsManagerTest extends AbstractCommonSetup { public class IslandsManagerTest extends AbstractCommonSetup {
private static AbstractDatabaseHandler<Object> h; private static AbstractDatabaseHandler<Object> h;
@ -386,6 +387,7 @@ public class IslandsManagerTest extends AbstractCommonSetup {
* {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. * {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}.
*/ */
@Test @Test
@Ignore("Material#isSolid() cannot be tested")
public void testIsSafeLocationSafe() { public void testIsSafeLocationSafe() {
assertTrue(im.isSafeLocation(location)); assertTrue(im.isSafeLocation(location));
} }
@ -403,6 +405,7 @@ public class IslandsManagerTest extends AbstractCommonSetup {
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}.
*/ */
@Test @Test
@Ignore("Material#isSolid() cannot be tested")
public void testIsSafeLocationNonSolidGround() { public void testIsSafeLocationNonSolidGround() {
when(ground.getType()).thenReturn(Material.WATER); when(ground.getType()).thenReturn(Material.WATER);
assertFalse(im.isSafeLocation(location)); assertFalse(im.isSafeLocation(location));
@ -412,6 +415,7 @@ public class IslandsManagerTest extends AbstractCommonSetup {
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}.
*/ */
@Test @Test
@Ignore("Material#isSolid() cannot be tested")
public void testIsSafeLocationSubmerged() { public void testIsSafeLocationSubmerged() {
when(ground.getType()).thenReturn(Material.STONE); when(ground.getType()).thenReturn(Material.STONE);
when(space1.getType()).thenReturn(Material.WATER); when(space1.getType()).thenReturn(Material.WATER);
@ -421,6 +425,7 @@ public class IslandsManagerTest extends AbstractCommonSetup {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Test @Test
@Ignore("Material#isSolid() cannot be tested")
public void testCheckIfSafeTrapdoor() { public void testCheckIfSafeTrapdoor() {
for (Material d : Material.values()) { for (Material d : Material.values()) {
if (d.name().contains("DOOR")) { if (d.name().contains("DOOR")) {
@ -437,6 +442,7 @@ public class IslandsManagerTest extends AbstractCommonSetup {
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}.
*/ */
@Test @Test
@Ignore("Material#isSolid() cannot be tested")
public void testIsSafeLocationPortals() { public void testIsSafeLocationPortals() {
when(ground.getType()).thenReturn(Material.STONE); when(ground.getType()).thenReturn(Material.STONE);
when(space1.getType()).thenReturn(Material.AIR); when(space1.getType()).thenReturn(Material.AIR);
@ -481,6 +487,7 @@ public class IslandsManagerTest extends AbstractCommonSetup {
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}.
*/ */
@Test @Test
@Ignore("Material#isSolid() cannot be tested")
public void testIsSafeLocationLava() { public void testIsSafeLocationLava() {
when(ground.getType()).thenReturn(Material.LAVA); when(ground.getType()).thenReturn(Material.LAVA);
when(space1.getType()).thenReturn(Material.AIR); when(space1.getType()).thenReturn(Material.AIR);
@ -500,6 +507,7 @@ public class IslandsManagerTest extends AbstractCommonSetup {
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}.
*/ */
@Test @Test
@Ignore("Material#isSolid() cannot be tested")
public void testTrapDoor() { public void testTrapDoor() {
when(ground.getType()).thenReturn(Material.OAK_TRAPDOOR); when(ground.getType()).thenReturn(Material.OAK_TRAPDOOR);
assertFalse("Open trapdoor", im.isSafeLocation(location)); assertFalse("Open trapdoor", im.isSafeLocation(location));
@ -511,6 +519,7 @@ public class IslandsManagerTest extends AbstractCommonSetup {
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}.
*/ */
@Test @Test
@Ignore("Material#isSolid() cannot be tested")
public void testBadBlocks() { public void testBadBlocks() {
// Fences // Fences
when(ground.getType()).thenReturn(Material.SPRUCE_FENCE); when(ground.getType()).thenReturn(Material.SPRUCE_FENCE);
@ -535,6 +544,7 @@ public class IslandsManagerTest extends AbstractCommonSetup {
* Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}. * Test method for {@link world.bentobox.bentobox.managers.IslandsManager#isSafeLocation(org.bukkit.Location)}.
*/ */
@Test @Test
@Ignore("Material#isSolid() cannot be tested")
public void testSolidBlocks() { public void testSolidBlocks() {
when(space1.getType()).thenReturn(Material.STONE); when(space1.getType()).thenReturn(Material.STONE);
assertFalse("Solid", im.isSafeLocation(location)); assertFalse("Solid", im.isSafeLocation(location));

View File

@ -137,6 +137,7 @@ public class IslandCacheTest extends AbstractCommonSetup {
when(island.getMemberSet()).thenReturn(members.build()); when(island.getMemberSet()).thenReturn(members.build());
when(island.getMinX()).thenReturn(-200); when(island.getMinX()).thenReturn(-200);
when(island.getMinZ()).thenReturn(-200); when(island.getMinZ()).thenReturn(-200);
when(island.getRange()).thenReturn(400);
// database must be mocked here // database must be mocked here
db = mock(Database.class); db = mock(Database.class);
@ -234,12 +235,10 @@ public class IslandCacheTest extends AbstractCommonSetup {
Location location2 = mock(Location.class); Location location2 = mock(Location.class);
when(location2.getWorld()).thenReturn(world); when(location2.getWorld()).thenReturn(world);
when(location2.getBlockX()).thenReturn(10); when(location2.getBlockX()).thenReturn(10000);
when(location2.getBlockY()).thenReturn(10); when(location2.getBlockY()).thenReturn(100);
when(location2.getBlockZ()).thenReturn(10); when(location2.getBlockZ()).thenReturn(10000);
assertEquals(island, ic.getIslandAt(location2));
when(island.inIslandSpace(any(Integer.class), any(Integer.class))).thenReturn(false);
assertNull(ic.getIslandAt(location2)); assertNull(ic.getIslandAt(location2));
} }

View File

@ -139,6 +139,7 @@ public class IslandCreationPanelTest {
when(ic.getAddon()).thenReturn(addon); when(ic.getAddon()).thenReturn(addon);
World world = mock(World.class); World world = mock(World.class);
when(ic.getWorld()).thenReturn(world); when(ic.getWorld()).thenReturn(world);
when(ic.getPlugin()).thenReturn(plugin);
// No island for player to begin with (set it later in the tests) // No island for player to begin with (set it later in the tests)
when(im.hasIsland(any(), eq(uuid))).thenReturn(false); when(im.hasIsland(any(), eq(uuid))).thenReturn(false);

View File

@ -108,6 +108,7 @@ public class LanguagePanelTest {
GameModeAddon addon = mock(GameModeAddon.class); GameModeAddon addon = mock(GameModeAddon.class);
when(command.getAddon()).thenReturn(addon); when(command.getAddon()).thenReturn(addon);
when(command.getPlugin()).thenReturn(plugin);
when(addon.getDataFolder()).thenReturn(resourcePath.toFile()); when(addon.getDataFolder()).thenReturn(resourcePath.toFile());
World world = mock(World.class); World world = mock(World.class);
@ -139,8 +140,6 @@ public class LanguagePanelTest {
} }
/**
*/
@After @After
public void tearDown() { public void tearDown() {
User.clearUsers(); User.clearUsers();

View File

@ -101,6 +101,12 @@ public class ItemParserTest {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }
@Override
public Keyed getOrThrow(NamespacedKey key) {
// TODO Auto-generated method stub
return null;
}
} }
@After @After