Skip to content

Commit

Permalink
Merge pull request #2537 from BentoBoxWorld/develop
Browse files Browse the repository at this point in the history
Release 2.7.0
  • Loading branch information
tastybento authored Oct 27, 2024
2 parents 0b1a205 + 41a2484 commit d29eb88
Show file tree
Hide file tree
Showing 49 changed files with 1,308 additions and 442 deletions.
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@
<postgresql.version>42.2.18</postgresql.version>
<hikaricp.version>5.0.1</hikaricp.version>
<!-- 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
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>
<vault.version>1.7.1</vault.version>
<placeholderapi.version>2.10.9</placeholderapi.version>
Expand All @@ -88,7 +88,7 @@
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- 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.host.url>https://sonarcloud.io</sonar.host.url>
<server.jars>${project.basedir}/lib</server.jars>
Expand Down
13 changes: 1 addition & 12 deletions src/main/java/world/bentobox/bentobox/BentoBox.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package world.bentobox.bentobox;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;

Expand Down Expand Up @@ -209,6 +207,7 @@ private void completeSetup(long loadTime) {
registerListeners();

// Load islands from database - need to wait until all the worlds are loaded
log("Loading islands from database...");
try {
islandsManager.load();
} catch (Exception e) {
Expand Down Expand Up @@ -466,16 +465,6 @@ public boolean loadSettings() {
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ private CompositeCommand getCommandFromArgs(String[] args) {
*
* @return IslandsManager
*/
protected IslandsManager getIslands() {
public IslandsManager getIslands() {
return plugin.getIslands();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package world.bentobox.bentobox.api.commands.admin.purge;

import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
Expand All @@ -17,16 +18,19 @@
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;

public class AdminPurgeCommand extends CompositeCommand implements Listener {

private static final Long YEAR2000 = 946713600L;
private static final int TOO_MANY = 1000;
private int count;
private boolean inPurge;
private boolean scanning;
private boolean toBeConfirmed;
private Iterator<String> it;
private User user;
private Set<String> islands = new HashSet<>();
private Set<Integer> loggedTiers = new HashSet<>(); // Set to store logged percentage tiers

public AdminPurgeCommand(CompositeCommand parent) {
super(parent, "purge");
Expand All @@ -47,6 +51,10 @@ public void setup() {

@Override
public boolean canExecute(User user, String label, List<String> args) {
if (scanning) {
user.sendMessage("commands.admin.purge.scanning-in-progress");
return false;
}
if (inPurge) {
user.sendMessage("commands.admin.purge.purge-in-progress", TextVariables.LABEL, this.getTopLabel());
return false;
Expand Down Expand Up @@ -75,13 +83,25 @@ public boolean execute(User user, String label, List<String> args) {
user.sendMessage("commands.admin.purge.days-one-or-more");
return false;
}
islands = getOldIslands(days);
user.sendMessage("commands.admin.purge.purgable-islands", TextVariables.NUMBER, String.valueOf(islands.size()));
if (!islands.isEmpty()) {
toBeConfirmed = true;
user.sendMessage("commands.admin.purge.confirm", TextVariables.LABEL, this.getTopLabel());
return false;
}
user.sendMessage("commands.admin.purge.scanning");
scanning = true;
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;
user.sendMessage("commands.admin.purge.confirm", TextVariables.LABEL, this.getTopLabel());
islands = islandSet;
} else {
user.sendMessage("commands.admin.purge.none-found");
}
scanning = false;
});

} catch (NumberFormatException e) {
user.sendMessage("commands.admin.purge.number-error");
return false;
Expand All @@ -94,6 +114,7 @@ void removeIslands() {
user.sendMessage("commands.admin.purge.see-console-for-status", TextVariables.LABEL, this.getTopLabel());
it = islands.iterator();
count = 0;
loggedTiers.clear(); // % reporting
// Delete first island
deleteIsland();
}
Expand All @@ -103,8 +124,21 @@ private void deleteIsland() {
getIslands().getIslandById(it.next()).ifPresent(i -> {
getIslands().deleteIsland(i, true, null);
count++;
String percentage = String.format("%.1f", (((float) count)/getPurgeableIslandsCount() * 100));
getPlugin().log(count + " islands purged out of " + getPurgeableIslandsCount() + " (" + percentage + " %)");
float percentage = ((float) count / getPurgeableIslandsCount()) * 100;
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 {
user.sendMessage("commands.admin.purge.completed");
Expand All @@ -116,7 +150,8 @@ private void deleteIsland() {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
void onIslandDeleted(IslandDeletedEvent e) {
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
}
}

Expand All @@ -125,40 +160,46 @@ void onIslandDeleted(IslandDeletedEvent e) {
* @param days days
* @return set of islands
*/
Set<String> getOldIslands(int days) {
long currentTimeMillis = System.currentTimeMillis();
long daysInMilliseconds = (long) days * 1000 * 3600 * 24;
Set<String> oldIslands = new HashSet<>();

CompletableFuture<Set<String>> getOldIslands(int days) {
CompletableFuture<Set<String>> result = new CompletableFuture<>();
// 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.getWorld() != null) // to handle currently unloaded world islands
.filter(i -> i.getWorld().equals(this.getWorld())).filter(Island::isOwned).filter(
i -> i.getMemberSet().stream()
.allMatch(member -> (currentTimeMillis
- Bukkit.getOfflinePlayer(member).getLastPlayed()) > daysInMilliseconds))
.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;
.filter(i -> i.getWorld().equals(this.getWorld())) // Island needs to be in this world
.filter(Island::isOwned) // The island needs to be owned
.filter(i -> i.getMemberSet().stream().allMatch(member -> checkLastLoginTimestamp(days, member)))
.forEach(i -> oldIslands.add(i.getUniqueId())); // Add the unique island ID to the set

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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ public boolean canExecute(User user, String label, List<String> args) {

@Override
public boolean execute(User user, String label, List<String> args) {
Map<String, IslandInfo> names = getNameIslandMap(user);
// Check if the home is known
if (!args.isEmpty()) {
Map<String, IslandInfo> names = getNameIslandMap(user);
final String name = String.join(" ", args);
if (!names.containsKey(name)) {
// Failed home name check
Expand Down Expand Up @@ -113,7 +113,11 @@ public Optional<List<String>> tabComplete(User user, String alias, List<String>

}

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) {
Map<String, IslandInfo> islandMap = new HashMap<>();
Expand All @@ -129,7 +133,8 @@ private Map<String, IslandInfo> getNameIslandMap(User user) {
islandMap.put(text, new IslandInfo(island, true));
}
// 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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
package world.bentobox.bentobox.api.commands.island;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

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.database.objects.Island;
import world.bentobox.bentobox.util.Util;
import world.bentobox.bentobox.panels.customizable.IslandHomesPanel;

public class IslandHomesCommand extends ConfirmableCommand {

private List<Island> islands;
public class IslandHomesCommand extends CompositeCommand {

public IslandHomesCommand(CompositeCommand islandCommand) {
super(islandCommand, "homes");
Expand All @@ -28,9 +21,8 @@ public void setup() {

@Override
public boolean canExecute(User user, String label, List<String> args) {
islands = getIslands().getIslands(getWorld(), user);
// Check island
if (islands.isEmpty()) {
if (getIslands().getIslands(getWorld(), user).isEmpty()) {
user.sendMessage("general.errors.no-island");
return false;
}
Expand All @@ -39,22 +31,8 @@ public boolean canExecute(User user, String label, List<String> args) {

@Override
public boolean execute(User user, String label, List<String> args) {
user.sendMessage("commands.island.sethome.homes-are");
islands.forEach(island ->
island.getHomes().keySet().stream().filter(s -> !s.isEmpty())
.forEach(s -> user.sendMessage("commands.island.sethome.home-list-syntax", TextVariables.NAME, s)));
IslandHomesPanel.openPanel(this, user);
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));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,8 @@ ItemSlot nextItemSlot() {
* this button is present.
*
* @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() {
return this.parentPanel.typeSlotMap;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,29 @@ public static PanelTemplateRecord readTemplatePanel(@NonNull String panelName, @
}

File file = new File(panelLocation, templateName.endsWith(YML) ? templateName : templateName + YML);

String absolutePath = file.getAbsolutePath();
if (!file.exists())
{
BentoBox.getInstance().logError(file.getAbsolutePath() + " does not exist for panel template");
// Return as file does not exist.
return null;
// 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");
// Return as file does not exist.
return null;
}

}

final String panelKey = file.getAbsolutePath() + ":" + panelName;
final String panelKey = absolutePath + ":" + panelName;

// Check if panel is already crafted.
if (TemplateReader.loadedPanels.containsKey(panelKey))
Expand Down
Loading

0 comments on commit d29eb88

Please sign in to comment.