Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@
/.classpath
/.project
/dependency-reduced-pom.xml
/*.iml
/*.iml
/dev.zip
/src.zip
30 changes: 15 additions & 15 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>dev.espi</groupId>
<artifactId>protectionstones</artifactId>
<version>2.10.6</version>
<version>2.10.8</version>
<name>ProtectionStones</name>
<description>A grief prevention plugin for Spigot Minecraft servers.</description>
<url>https://github.com/espidev/ProtectionStones</url>
Expand Down Expand Up @@ -141,19 +141,19 @@
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.sonatype.plugins</groupId>
Expand Down Expand Up @@ -207,7 +207,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.10-R0.1-SNAPSHOT</version>
<version>26.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,27 @@ class ArgAdminCleanup {
private static File previewFile;
private static FileWriter previewFileOutputStream;

// /ps admin cleanup [remove/preview]
static boolean argumentAdminCleanup(CommandSender p, String[] preParseArgs) {
if (preParseArgs.length < 3 || !Arrays.asList("remove", "preview").contains(preParseArgs[2].toLowerCase())) {
PSL.msg(p, ArgAdmin.getCleanupHelp());
return true;
}

String cleanupOperation = preParseArgs[2].toLowerCase(); // [remove|preview]
String cleanupOperation = preParseArgs[2].toLowerCase();

World w;
String alias = null;

List<String> args = new ArrayList<>();

// determine if there is an alias flag selected, and remove [-t typealias] if there is
for (int i = 3; i < preParseArgs.length; i++) {
if (preParseArgs[i].equals("-t") && i != preParseArgs.length-1) {
if (preParseArgs[i].equals("-t") && i != preParseArgs.length - 1) {
alias = preParseArgs[++i];
} else {
args.add(preParseArgs[i]);
}
}

// the args array should consist of: [days, world (optional)]
if (args.size() > 1 && Bukkit.getWorld(args.get(1)) != null) {
w = Bukkit.getWorld(args.get(1));
} else {
Expand All @@ -72,87 +69,101 @@ static boolean argumentAdminCleanup(CommandSender p, String[] preParseArgs) {
}
}

// create preview file
int days;
try {
days = (args.size() > 0) ? Integer.parseInt(args.get(0)) : 30;
} catch (Exception e) {
PSL.msg(p, PSL.ADMIN_ERROR_PARSING.msg());
return true;
}

if (cleanupOperation.equals("preview")) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd H-m-s");
previewFile = new File(ProtectionStones.getInstance().getDataFolder().getAbsolutePath() + "/" + LocalDateTime.now().format(formatter) + " cleanup preview.txt");
try {
previewFile.createNewFile();
previewFileOutputStream = new FileWriter(previewFile);
} catch (IOException e) {
e.printStackTrace();
PSL.;
p.sendMessage(ChatColor.RED + "Internal error, please check the console logs.");
return true;
}
}

RegionManager rgm = WGUtils.getRegionManagerWithWorld(w);
Map<String, ProtectedRegion> regions = rgm.getRegions();
if (rgm == null) {
PSL.msg(p, PSL.INVALID_WORLD.msg());
return true;
}

// async cleanup task
Map<String, ProtectedRegion> regions = new HashMap<>(rgm.getRegions());
String finalAlias = alias;
Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), () -> {
int days = (args.size() > 0) ? Integer.parseInt(args.get(0)) : 30; // 30 days is default if days aren't specified
int finalDays = days;

Bukkit.getScheduler().runTaskAsynchronously(ProtectionStones.getInstance(), () -> {
PSL.msg(p, PSL.ADMIN_CLEANUP_HEADER.msg()
.replace("%arg%", cleanupOperation)
.replace("%days%", "" + days));
.replace("%days%", "" + finalDays));

HashSet<UUID> relevantPlayers = new HashSet<>();
List<PSRegion> psRegions = new ArrayList<>();

for (ProtectedRegion protectedRegion : regions.values()) {
PSRegion r = PSRegion.fromWGRegion(w, protectedRegion);
if (r == null) {
continue;
}

if (finalAlias != null && (r.getTypeOptions() == null || !r.getTypeOptions().alias.equals(finalAlias))) {
continue;
}

psRegions.add(r);
relevantPlayers.addAll(r.getOwners());
relevantPlayers.addAll(r.getMembers());
}

HashSet<UUID> activePlayers = new HashSet<>();
long now = System.currentTimeMillis();

// loop over offline players and add to list if they haven't joined recently
for (OfflinePlayer op : Bukkit.getServer().getOfflinePlayers()) {
long lastPlayed = (System.currentTimeMillis() - op.getLastPlayed()) / 86400000L;
for (UUID uuid : relevantPlayers) {
try {
// a player is active if they have joined within the days
if (lastPlayed < days) {
activePlayers.add(op.getUniqueId());
OfflinePlayer op = Bukkit.getOfflinePlayer(uuid);
long lastPlayed = (now - op.getLastPlayed()) / 86400000L;
if (lastPlayed < finalDays) {
activePlayers.add(uuid);
}
} catch (Exception e) {
e.printStackTrace();
}
}

// loop over all regions async and find regions to delete
List<PSRegion> toDelete = new ArrayList<>();
for (String regionId : regions.keySet()) {
PSRegion r = PSRegion.fromWGRegion(w, regions.get(regionId));
if (r == null) { // not a ps region (unconfigured types still count as ps regions)
continue;
}

// if an alias is specified, skip regions that aren't of the type
if (finalAlias != null && (r.getTypeOptions() == null || !r.getTypeOptions().alias.equals(finalAlias))) {
continue;
}

for (PSRegion r : psRegions) {
long numOfActiveOwners = r.getOwners().stream().filter(activePlayers::contains).count();
long numOfActiveMembers = r.getMembers().stream().filter(activePlayers::contains).count();

// remove region if there are no owners left
if (numOfActiveOwners == 0) {
if (ProtectionStones.getInstance().getConfigOptions().cleanupDeleteRegionsWithMembersButNoOwners || numOfActiveMembers == 0) {
toDelete.add(r);
}
}
}

// start recursive iteration to delete a region each tick
Iterator<PSRegion> deleteRegionsIterator = toDelete.iterator();
regionLoop(deleteRegionsIterator, p, cleanupOperation.equalsIgnoreCase("remove"));
});
return true;
return false;
}

static private void regionLoop(Iterator<PSRegion> deleteRegionsIterator, CommandSender p, boolean isRemoveOperation) {
if (deleteRegionsIterator.hasNext()) {
Bukkit.getScheduler().runTaskLater(ProtectionStones.getInstance(), () ->
processRegion(deleteRegionsIterator, p, isRemoveOperation), 1);
} else { // finished region iteration
} else {
PSL.msg(p, PSL.ADMIN_CLEANUP_FOOTER.msg()
.replace("%arg%", isRemoveOperation ? "remove" : "preview"));

// flush and close preview file
if (!isRemoveOperation) {
try {
p.sendMessage(ChatColor.YELLOW + "Dumped the list regions that can be deleted in " + previewFile.getName() + " (in the plugin folder).");
Expand All @@ -165,31 +176,22 @@ static private void regionLoop(Iterator<PSRegion> deleteRegionsIterator, Command
}
}

// Process a region, and then iterate to the next region on the next tick.
// This is to prevent the server from pausing for the entire duration of the cleanup.
// (lag from loading chunks to remove protection blocks)
static private void processRegion(Iterator<PSRegion> deleteRegionsIterator, CommandSender p, boolean isRemoveOperation) {
PSRegion r = deleteRegionsIterator.next();

if (isRemoveOperation) { // delete

if (isRemoveOperation) {
p.sendMessage(ChatColor.YELLOW + "Removed region " + r.getId() + " due to inactive owners.");

// must be sync
r.deleteRegion(true);
} else { // preview

} else {
p.sendMessage(ChatColor.YELLOW + "Found region " + r.getId() + " that can be deleted.");

// adds region id to preview file
try {
previewFileOutputStream.write(r.getId() + "\n");
} catch (IOException e) {
e.printStackTrace();
}
}

// go to next region
regionLoop(deleteRegionsIterator, p, isRemoveOperation);
}
}
Loading
Loading