Skip to content

Commit ff8676c

Browse files
authored
Execute chunk loading operations outside the main thread (#4522)
* Wait with further command execution until chunk will be loaded * Replaced `Throwable#printStackTraces` with `Logger#error` * Moved from jetbrains to javax annotations * Use preferred logging
1 parent 817effb commit ff8676c

File tree

1 file changed

+158
-94
lines changed

1 file changed

+158
-94
lines changed

Core/src/main/java/com/plotsquared/core/command/MainCommand.java

Lines changed: 158 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,13 @@
4444
import java.util.Arrays;
4545
import java.util.LinkedList;
4646
import java.util.List;
47+
import java.util.Objects;
48+
import java.util.Optional;
4749
import java.util.concurrent.CompletableFuture;
4850

51+
import javax.annotation.Nonnull;
52+
import javax.annotation.Nullable;
53+
4954
/**
5055
* PlotSquared command class.
5156
*/
@@ -147,8 +152,7 @@ public static MainCommand getInstance() {
147152
try {
148153
injector.getInstance(command);
149154
} catch (final Exception e) {
150-
LOGGER.error("Failed to register command {}", command.getCanonicalName());
151-
e.printStackTrace();
155+
LOGGER.error("Failed to register command {}", command.getCanonicalName(), e);
152156
}
153157
}
154158

@@ -236,111 +240,171 @@ public CompletableFuture<Boolean> execute(
236240
RunnableVal3<Command, Runnable, Runnable> confirm,
237241
RunnableVal2<Command, CommandResult> whenDone
238242
) {
239-
// Optional command scope //
240-
Location location = null;
241-
Plot plot = null;
242-
boolean tp = false;
243-
if (args.length >= 2) {
244-
PlotArea area = player.getApplicablePlotArea();
245-
Plot newPlot = Plot.fromString(area, args[0]);
246-
if (newPlot != null && (player instanceof ConsolePlayer || newPlot.getArea()
247-
.equals(area) || player.hasPermission(Permission.PERMISSION_ADMIN)
248-
|| player.hasPermission(Permission.PERMISSION_ADMIN_AREA_SUDO))
249-
&& !newPlot.isDenied(player.getUUID())) {
250-
final Location newLoc;
251-
if (newPlot.getArea() instanceof SinglePlotArea) {
252-
newLoc = newPlot.isLoaded() ? newPlot.getCenterSynchronous() : Location.at("", 0, 0, 0);
253-
} else {
254-
newLoc = newPlot.getCenterSynchronous();
255-
}
256-
if (player.canTeleport(newLoc)) {
257-
// Save meta
258-
try (final MetaDataAccess<Location> locationMetaDataAccess
259-
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
260-
location = locationMetaDataAccess.get().orElse(null);
261-
locationMetaDataAccess.set(newLoc);
243+
prepareArguments(new CommandExecutionData(player, args, confirm, whenDone, null))
244+
.thenCompose(executionData -> {
245+
if (executionData.isEmpty()) {
246+
return CompletableFuture.completedFuture(false);
262247
}
263-
try (final MetaDataAccess<Plot> plotMetaDataAccess
264-
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
265-
plot = plotMetaDataAccess.get().orElse(null);
266-
plotMetaDataAccess.set(newPlot);
248+
var data = executionData.get();
249+
try {
250+
return super.execute(data.player(), data.args(), data.confirm(), data.whenDone());
251+
} catch (CommandException e) {
252+
throw e;
253+
} catch (Throwable e) {
254+
LOGGER.error("A error occurred while executing plot command", e);
255+
String message = e.getMessage();
256+
if (message != null) {
257+
data.player().sendMessage(
258+
TranslatableCaption.of("errors.error"),
259+
TagResolver.resolver("value", Tag.inserting(Component.text(message)))
260+
);
261+
} else {
262+
data.player().sendMessage(
263+
TranslatableCaption.of("errors.error_console"));
264+
}
265+
} finally {
266+
if (data.postCommandData() != null) {
267+
resetCommandScope(data.player(), data.postCommandData());
268+
}
267269
}
268-
tp = true;
269-
} else {
270-
player.sendMessage(TranslatableCaption.of("border.denied"));
271-
return CompletableFuture.completedFuture(false);
272-
}
273-
// Trim command
274-
args = Arrays.copyOfRange(args, 1, args.length);
275-
}
276-
if (args.length >= 2 && !args[0].isEmpty() && args[0].charAt(0) == '-') {
277-
if ("f".equals(args[0].substring(1))) {
278-
confirm = new RunnableVal3<>() {
279-
@Override
280-
public void run(Command cmd, Runnable success, Runnable failure) {
281-
if (area != null && PlotSquared.platform().econHandler().isEnabled(area)) {
282-
PlotExpression priceEval =
283-
area.getPrices().get(cmd.getFullId());
284-
double price = priceEval != null ? priceEval.evaluate(0d) : 0d;
285-
if (price != 0d
286-
&& PlotSquared.platform().econHandler().getMoney(player) < price) {
287-
if (failure != null) {
288-
failure.run();
289-
}
290-
return;
291-
}
292-
}
293-
if (success != null) {
294-
success.run();
295-
}
270+
return CompletableFuture.completedFuture(true);
271+
});
272+
return CompletableFuture.completedFuture(true);
273+
}
274+
275+
private CompletableFuture<Optional<CommandExecutionData>> prepareArguments(CommandExecutionData data) {
276+
if (data.args().length >= 2) {
277+
PlotArea area = data.player().getApplicablePlotArea();
278+
Plot newPlot = Plot.fromString(area, data.args()[0]);
279+
return preparePlotArgument(newPlot, data, area)
280+
.thenApply(d -> d.flatMap(x -> prepareFlagArgument(x, area)));
281+
} else {
282+
return CompletableFuture.completedFuture(Optional.of(data));
283+
}
284+
}
285+
286+
private CompletableFuture<Optional<CommandExecutionData>> preparePlotArgument(@Nullable Plot newPlot,
287+
@Nonnull CommandExecutionData data,
288+
@Nullable PlotArea area) {
289+
if (newPlot != null && (data.player() instanceof ConsolePlayer
290+
|| (area != null && area.equals(newPlot.getArea()))
291+
|| data.player().hasPermission(Permission.PERMISSION_ADMIN)
292+
|| data.player().hasPermission(Permission.PERMISSION_ADMIN_AREA_SUDO))
293+
&& !newPlot.isDenied(data.player().getUUID())) {
294+
return fetchPlotCenterLocation(newPlot)
295+
.thenApply(newLoc -> {
296+
if (!data.player().canTeleport(newLoc)) {
297+
data.player().sendMessage(TranslatableCaption.of("border.denied"));
298+
return Optional.empty();
296299
}
297-
};
298-
args = Arrays.copyOfRange(args, 1, args.length);
299-
} else {
300-
player.sendMessage(TranslatableCaption.of("errors.invalid_command_flag"));
301-
return CompletableFuture.completedFuture(false);
302-
}
303-
}
300+
// Save meta
301+
var originalCommandMeta = setCommandScope(data.player(), new TemporaryCommandMeta(newLoc, newPlot));
302+
return Optional.of(new CommandExecutionData(
303+
data.player(),
304+
Arrays.copyOfRange(data.args(), 1, data.args().length), // Trimmed command
305+
data.confirm(),
306+
data.whenDone(),
307+
originalCommandMeta
308+
));
309+
});
304310
}
305-
try {
306-
super.execute(player, args, confirm, whenDone);
307-
} catch (CommandException e) {
308-
throw e;
309-
} catch (Throwable e) {
310-
e.printStackTrace();
311-
String message = e.getMessage();
312-
if (message != null) {
313-
player.sendMessage(
314-
TranslatableCaption.of("errors.error"),
315-
TagResolver.resolver("value", Tag.inserting(Component.text(message)))
316-
);
311+
return CompletableFuture.completedFuture(Optional.of(data));
312+
}
313+
314+
private Optional<CommandExecutionData> prepareFlagArgument(@Nonnull CommandExecutionData data, @Nonnull PlotArea area) {
315+
if (data.args().length >= 2 && !data.args()[0].isEmpty() && data.args()[0].charAt(0) == '-') {
316+
if ("f".equals(data.args()[0].substring(1))) {
317+
return Optional.of(new CommandExecutionData(
318+
data.player(),
319+
Arrays.copyOfRange(data.args(), 1, data.args().length), // Trimmed command
320+
createForcedConfirmation(data.player(), area),
321+
data.whenDone(),
322+
data.postCommandData()
323+
));
317324
} else {
318-
player.sendMessage(
319-
TranslatableCaption.of("errors.error_console"));
325+
data.player().sendMessage(TranslatableCaption.of("errors.invalid_command_flag"));
326+
return Optional.empty();
320327
}
321328
}
322-
// Reset command scope //
323-
if (tp && !(player instanceof ConsolePlayer)) {
324-
try (final MetaDataAccess<Location> locationMetaDataAccess
325-
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
326-
if (location == null) {
327-
locationMetaDataAccess.remove();
328-
} else {
329-
locationMetaDataAccess.set(location);
329+
return Optional.of(data);
330+
}
331+
332+
private CompletableFuture<Location> fetchPlotCenterLocation(Plot plot) {
333+
if (plot.getArea() instanceof SinglePlotArea && !plot.isLoaded()) {
334+
return CompletableFuture.completedFuture(Location.at("", 0, 0, 0));
335+
}
336+
CompletableFuture<Location> future = new CompletableFuture<>();
337+
plot.getCenter(future::complete);
338+
return future;
339+
}
340+
341+
private @Nonnull RunnableVal3<Command, Runnable, Runnable> createForcedConfirmation(@Nonnull PlotPlayer<?> player,
342+
@Nullable PlotArea area) {
343+
return new RunnableVal3<>() {
344+
@Override
345+
public void run(Command cmd, Runnable success, Runnable failure) {
346+
if (area != null && PlotSquared.platform().econHandler().isEnabled(area)
347+
&& Optional.of(area.getPrices().get(cmd.getFullId()))
348+
.map(priceEval -> priceEval.evaluate(0d))
349+
.filter(price -> price != 0d)
350+
.filter(price -> PlotSquared.platform().econHandler().getMoney(player) < price)
351+
.isPresent()) {
352+
if (failure != null) {
353+
failure.run();
354+
}
355+
return;
330356
}
331-
}
332-
try (final MetaDataAccess<Plot> plotMetaDataAccess
333-
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
334-
if (plot == null) {
335-
plotMetaDataAccess.remove();
336-
} else {
337-
plotMetaDataAccess.set(plot);
357+
if (success != null) {
358+
success.run();
338359
}
339360
}
361+
};
362+
}
363+
364+
private @Nonnull TemporaryCommandMeta setCommandScope(@Nonnull PlotPlayer<?> player, @Nonnull TemporaryCommandMeta commandMeta) {
365+
Objects.requireNonNull(commandMeta.location());
366+
Objects.requireNonNull(commandMeta.plot());
367+
Location location;
368+
Plot plot;
369+
try (final MetaDataAccess<Location> locationMetaDataAccess
370+
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
371+
location = locationMetaDataAccess.get().orElse(null);
372+
locationMetaDataAccess.set(commandMeta.location());
373+
}
374+
try (final MetaDataAccess<Plot> plotMetaDataAccess
375+
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
376+
plot = plotMetaDataAccess.get().orElse(null);
377+
plotMetaDataAccess.set(commandMeta.plot());
378+
}
379+
return new TemporaryCommandMeta(location, plot);
380+
}
381+
382+
private void resetCommandScope(@Nonnull PlotPlayer<?> player, @Nonnull TemporaryCommandMeta commandMeta) {
383+
try (final MetaDataAccess<Location> locationMetaDataAccess
384+
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
385+
if (commandMeta.location() == null) {
386+
locationMetaDataAccess.remove();
387+
} else {
388+
locationMetaDataAccess.set(commandMeta.location());
389+
}
390+
}
391+
try (final MetaDataAccess<Plot> plotMetaDataAccess
392+
= player.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
393+
if (commandMeta.plot() == null) {
394+
plotMetaDataAccess.remove();
395+
} else {
396+
plotMetaDataAccess.set(commandMeta.plot());
397+
}
340398
}
341-
return CompletableFuture.completedFuture(true);
342399
}
343400

401+
private record CommandExecutionData(@Nonnull PlotPlayer<?> player, @Nonnull String[] args,
402+
@Nonnull RunnableVal3<Command, Runnable, Runnable> confirm,
403+
@Nonnull RunnableVal2<Command, CommandResult> whenDone,
404+
@Nullable TemporaryCommandMeta postCommandData) {}
405+
406+
private record TemporaryCommandMeta(@Nullable Location location, @Nullable Plot plot) {}
407+
344408
@Override
345409
public boolean canExecute(PlotPlayer<?> player, boolean message) {
346410
return true;

0 commit comments

Comments
 (0)