Skip to content

Commit 3a280a2

Browse files
authored
Merge pull request #2949 from BentoBoxWorld/feature/merge-purge-regions
Merge /purge regions into /purge; drop status/stop; simplify /purge unowned
2 parents 60311a2 + 64aae80 commit 3a280a2

File tree

12 files changed

+537
-1402
lines changed

12 files changed

+537
-1402
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package world.bentobox.bentobox.api.commands.admin.purge;
2+
3+
import java.util.Optional;
4+
import java.util.Set;
5+
import java.util.function.Supplier;
6+
import java.util.stream.Collectors;
7+
8+
import org.bukkit.Bukkit;
9+
import org.bukkit.World;
10+
11+
import world.bentobox.bentobox.api.commands.CompositeCommand;
12+
import world.bentobox.bentobox.api.localization.TextVariables;
13+
import world.bentobox.bentobox.api.user.User;
14+
import world.bentobox.bentobox.database.objects.Island;
15+
import world.bentobox.bentobox.managers.PurgeRegionsService.PurgeScanResult;
16+
17+
/**
18+
* Shared scaffolding for purge subcommands that scan for region files and
19+
* (on confirmation) reap them via {@link world.bentobox.bentobox.managers.PurgeRegionsService}.
20+
*
21+
* <p>Subclasses supply the scan source (age-filtered or deletable-only),
22+
* confirmation prompt, success message and any pre-delete side-effects via
23+
* the abstract / overridable hooks.
24+
*/
25+
abstract class AbstractPurgeCommand extends CompositeCommand {
26+
27+
protected static final String NONE_FOUND = "commands.admin.purge.none-found";
28+
29+
protected volatile boolean inPurge;
30+
protected boolean toBeConfirmed;
31+
protected User user;
32+
protected PurgeScanResult lastScan;
33+
34+
protected AbstractPurgeCommand(CompositeCommand parent, String label) {
35+
super(parent, label);
36+
}
37+
38+
@Override
39+
public boolean canExecute(User user, String label, java.util.List<String> args) {
40+
if (inPurge) {
41+
user.sendMessage("commands.admin.purge.purge-in-progress", TextVariables.LABEL, this.getTopLabel());
42+
return false;
43+
}
44+
return true;
45+
}
46+
47+
/**
48+
* Saves all worlds, runs the scan on an async thread, stores the result
49+
* in {@link #lastScan} and prompts for confirmation if non-empty.
50+
*/
51+
protected final void runScanAndPrompt(Supplier<PurgeScanResult> scanFn) {
52+
user.sendMessage("commands.admin.purge.scanning");
53+
getPlugin().log(logPrefix() + ": saving all worlds before scanning...");
54+
Bukkit.getWorlds().forEach(World::save);
55+
getPlugin().log(logPrefix() + ": world save complete");
56+
57+
inPurge = true;
58+
Bukkit.getScheduler().runTaskAsynchronously(getPlugin(), () -> {
59+
try {
60+
lastScan = scanFn.get();
61+
displayResultsAndPrompt(lastScan);
62+
} finally {
63+
inPurge = false;
64+
}
65+
});
66+
}
67+
68+
/**
69+
* Confirm path: save worlds, run any pre-delete hook, then dispatch the
70+
* region-file deletion asynchronously.
71+
*/
72+
protected final boolean deleteEverything() {
73+
if (lastScan == null || lastScan.isEmpty()) {
74+
user.sendMessage(NONE_FOUND);
75+
return false;
76+
}
77+
PurgeScanResult scan = lastScan;
78+
lastScan = null;
79+
toBeConfirmed = false;
80+
getPlugin().log(logPrefix() + ": saving all worlds before deleting region files...");
81+
Bukkit.getWorlds().forEach(World::save);
82+
beforeDelete(scan);
83+
getPlugin().log(logPrefix() + ": world save complete, dispatching deletion");
84+
Bukkit.getScheduler().runTaskAsynchronously(getPlugin(), () -> {
85+
boolean ok = getPlugin().getPurgeRegionsService().delete(scan);
86+
Bukkit.getScheduler().runTask(getPlugin(), () -> {
87+
if (ok) {
88+
user.sendMessage(successMessageKey());
89+
} else {
90+
getPlugin().log(logPrefix() + ": failed to delete one or more region files");
91+
user.sendMessage("commands.admin.purge.failed");
92+
}
93+
});
94+
});
95+
return true;
96+
}
97+
98+
private void displayResultsAndPrompt(PurgeScanResult scan) {
99+
Set<Island> uniqueIslands = scan.deletableRegions().values().stream()
100+
.flatMap(Set::stream)
101+
.map(getPlugin().getIslands()::getIslandById)
102+
.flatMap(Optional::stream)
103+
.collect(Collectors.toSet());
104+
105+
logScanContents(uniqueIslands, scan);
106+
107+
if (scan.isEmpty()) {
108+
Bukkit.getScheduler().runTask(getPlugin(), () -> user.sendMessage(NONE_FOUND));
109+
} else {
110+
Bukkit.getScheduler().runTask(getPlugin(), () -> {
111+
user.sendMessage("commands.admin.purge.purgable-islands",
112+
TextVariables.NUMBER, String.valueOf(uniqueIslands.size()));
113+
sendConfirmPrompt();
114+
toBeConfirmed = true;
115+
});
116+
}
117+
}
118+
119+
/** Log prefix used across the scan and delete phases (e.g. {@code "Purge"}). */
120+
protected abstract String logPrefix();
121+
122+
/** Locale key sent to the user when delete completes successfully. */
123+
protected abstract String successMessageKey();
124+
125+
/** Send any subclass-specific confirmation prompt(s). Runs on the main thread. */
126+
protected abstract void sendConfirmPrompt();
127+
128+
/** Hook invoked on the main thread immediately before the async delete. Default: no-op. */
129+
protected void beforeDelete(PurgeScanResult scan) {
130+
// no-op
131+
}
132+
133+
/** Hook invoked off-thread to log scan contents before the prompt. Default: no-op. */
134+
protected void logScanContents(Set<Island> uniqueIslands, PurgeScanResult scan) {
135+
// no-op
136+
}
137+
}

0 commit comments

Comments
 (0)