mirror of
https://github.com/EngineHub/WorldGuard.git
synced 2024-11-15 07:05:32 +01:00
Remove AsyncCommandHelper for new AsyncCommandBuilder.
Helper suffers from race conditions for short-lived tasks, leading to some poor UX conditions such as errors not propagating to the user (because the exception handler wasn't attached to the future yet), or lack of success messages. This commit replaces that system by a Builder which takes a callable to begin, and then takes supervisor, delay message, and the success and failure messages and handlers as parts of the builder. The success and failure handlers wrap the callable itself before submitting to the executor so they will always be run. The supervisor and delay are added as listeners to the future since they aren't required if the task is sufficiently short-lived (and to maintain compatibility with the classes which are now in WorldEdit). The builder also supports Components for success and failure messages, as well as consumers of the callable's result or exception for better customization of output, instead of having to rely on adding a callback to the future. The future is still returned for certain special usages.
This commit is contained in:
parent
e7ef6af012
commit
d542ba78ff
@ -29,7 +29,6 @@
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
||||
import com.sk89q.minecraft.util.commands.NestedCommand;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.command.util.AsyncCommandHelper;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.util.auth.AuthorizationException;
|
||||
@ -41,6 +40,7 @@
|
||||
import com.sk89q.worldedit.util.paste.ActorCallbackPaste;
|
||||
import com.sk89q.worldedit.util.report.ReportList;
|
||||
import com.sk89q.worldedit.util.report.SystemInfoReport;
|
||||
import com.sk89q.worldedit.util.task.FutureForwardingTask;
|
||||
import com.sk89q.worldedit.util.task.Task;
|
||||
import com.sk89q.worldedit.util.task.TaskStateComparator;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
@ -142,7 +142,7 @@ public void report(CommandContext args, final Actor sender) throws CommandExcept
|
||||
|
||||
if (args.hasFlag('p')) {
|
||||
sender.checkPermission("worldguard.report.pastebin");
|
||||
ActorCallbackPaste.pastebin(worldGuard.getSupervisor(), sender, result, "WorldGuard report: %s.report", worldGuard.getExceptionConverter());
|
||||
ActorCallbackPaste.pastebin(worldGuard.getSupervisor(), sender, result, "WorldGuard report: %s.report");
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,14 +196,12 @@ public void profile(final CommandContext args, final Actor sender) throws Comman
|
||||
}
|
||||
|
||||
sender.print(TextComponent.of("Starting CPU profiling. Use ", TextColor.LIGHT_PURPLE)
|
||||
.append(TextComponent.of("/wg stopprofle", TextColor.AQUA)
|
||||
.append(TextComponent.of("/wg stopprofile", TextColor.AQUA)
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.RUN_COMMAND, "/wg stopprofile")))
|
||||
.append(TextComponent.of(" to stop profling.", TextColor.LIGHT_PURPLE)));
|
||||
AsyncCommandHelper.wrap(sampler.getFuture(), worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.formatUsing(minutes)
|
||||
.registerWithSupervisor("Running CPU profiler for %d minute(s)...")
|
||||
.sendMessageAfterDelay("(Please wait... profiling for %d minute(s)...)")
|
||||
.thenTellErrorsOnly("CPU profiling failed");
|
||||
.append(TextComponent.of(" to stop profiling.", TextColor.LIGHT_PURPLE)));
|
||||
|
||||
worldGuard.getSupervisor().monitor(FutureForwardingTask.create(
|
||||
sampler.getFuture(), "CPU profiling for " + minutes + " minutes", sender));
|
||||
|
||||
sampler.getFuture().addListener(() -> {
|
||||
synchronized (WorldGuardCommands.this) {
|
||||
@ -225,7 +223,7 @@ public void onSuccess(Sampler result) {
|
||||
}
|
||||
|
||||
if (pastebin) {
|
||||
ActorCallbackPaste.pastebin(worldGuard.getSupervisor(), sender, output, "Profile result: %s.profile", worldGuard.getExceptionConverter());
|
||||
ActorCallbackPaste.pastebin(worldGuard.getSupervisor(), sender, output, "Profile result: %s.profile");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ class FlagHelperBox extends PaginationBox {
|
||||
private final RegionPermissionModel perms;
|
||||
|
||||
FlagHelperBox(World world, ProtectedRegion region, RegionPermissionModel perms) {
|
||||
super("Flags for " + region.getId(), "/rg flags -w " + world.getName() + " " + region.getId() + " %page%");
|
||||
super("Flags for " + region.getId(), "/rg flags -w " + world.getName() + " -p %page% " + region.getId());
|
||||
this.world = world;
|
||||
this.region = region;
|
||||
this.perms = perms;
|
||||
|
@ -19,13 +19,11 @@
|
||||
|
||||
package com.sk89q.worldguard.commands.region;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.sk89q.minecraft.util.commands.Command;
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
|
||||
import com.sk89q.worldedit.command.util.AsyncCommandHelper;
|
||||
import com.sk89q.worldedit.command.util.AsyncCommandBuilder;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.util.auth.AuthorizationException;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
@ -37,6 +35,8 @@
|
||||
import com.sk89q.worldguard.protection.util.DomainInputResolver;
|
||||
import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class MemberCommands extends RegionCommandsBase {
|
||||
|
||||
private final WorldGuard worldGuard;
|
||||
@ -68,16 +68,13 @@ public void addMember(CommandContext args, Actor sender) throws CommandException
|
||||
WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0));
|
||||
resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY);
|
||||
|
||||
// Then add it to the members
|
||||
ListenableFuture<DefaultDomain> future = Futures.transform(
|
||||
WorldGuard.getInstance().getExecutorService().submit(resolver),
|
||||
resolver.createAddAllFunction(region.getMembers()));
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.formatUsing(region.getId(), world.getName())
|
||||
.registerWithSupervisor("Adding members to the region '%s' on '%s'")
|
||||
.sendMessageAfterDelay("(Please wait... querying player names...)")
|
||||
.thenRespondWith("Region '%s' updated with new members.", "Failed to add new members");
|
||||
final String description = String.format("Adding members to the region '%s' on '%s'", region.getId(), world.getName());
|
||||
AsyncCommandBuilder.wrap(resolver, sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), description)
|
||||
.onSuccess(String.format("Region '%s' updated with new members.", region.getId()), region.getMembers()::addAll)
|
||||
.onFailure("Failed to add new members", worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
}
|
||||
|
||||
@Command(aliases = {"addowner", "addowner", "ao"},
|
||||
@ -127,16 +124,13 @@ public void addOwner(CommandContext args, Actor sender) throws CommandException,
|
||||
WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0));
|
||||
resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY);
|
||||
|
||||
// Then add it to the owners
|
||||
ListenableFuture<DefaultDomain> future = Futures.transform(
|
||||
WorldGuard.getInstance().getExecutorService().submit(resolver),
|
||||
resolver.createAddAllFunction(region.getOwners()));
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.formatUsing(region.getId(), world.getName())
|
||||
.registerWithSupervisor("Adding owners to the region '%s' on '%s'")
|
||||
.sendMessageAfterDelay("(Please wait... querying player names...)")
|
||||
.thenRespondWith("Region '%s' updated with new owners.", "Failed to add new owners");
|
||||
final String description = String.format("Adding owners to the region '%s' on '%s'", region.getId(), world.getName());
|
||||
AsyncCommandBuilder.wrap(resolver, sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), description)
|
||||
.onSuccess(String.format("Region '%s' updated with new owners.", region.getId()), region.getOwners()::addAll)
|
||||
.onFailure("Failed to add new owners", worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
}
|
||||
|
||||
@Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"},
|
||||
@ -157,12 +151,9 @@ public void removeMember(CommandContext args, Actor sender) throws CommandExcept
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
|
||||
ListenableFuture<?> future;
|
||||
|
||||
Callable<DefaultDomain> callable;
|
||||
if (args.hasFlag('a')) {
|
||||
region.getMembers().removeAll();
|
||||
|
||||
future = Futures.immediateFuture(null);
|
||||
callable = region::getMembers;
|
||||
} else {
|
||||
if (args.argsLength() < 2) {
|
||||
throw new CommandException("List some names to remove, or use -a to remove all.");
|
||||
@ -173,17 +164,16 @@ public void removeMember(CommandContext args, Actor sender) throws CommandExcept
|
||||
WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0));
|
||||
resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME);
|
||||
|
||||
// Then remove it from the members
|
||||
future = Futures.transform(
|
||||
WorldGuard.getInstance().getExecutorService().submit(resolver),
|
||||
resolver.createRemoveAllFunction(region.getMembers()));
|
||||
callable = resolver;
|
||||
}
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.formatUsing(region.getId(), world.getName())
|
||||
.registerWithSupervisor("Removing members from the region '%s' on '%s'")
|
||||
final String description = String.format("Removing members from the region '%s' on '%s'", region.getId(), world.getName());
|
||||
AsyncCommandBuilder.wrap(callable, sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), description)
|
||||
.sendMessageAfterDelay("(Please wait... querying player names...)")
|
||||
.thenRespondWith("Region '%s' updated with members removed.", "Failed to remove members");
|
||||
.onSuccess(String.format("Region '%s' updated with members removed.", region.getId()), region.getMembers()::removeAll)
|
||||
.onFailure("Failed to remove members", worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
}
|
||||
|
||||
@Command(aliases = {"removeowner", "remowner", "ro"},
|
||||
@ -204,12 +194,9 @@ public void removeOwner(CommandContext args, Actor sender) throws CommandExcepti
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
|
||||
ListenableFuture<?> future;
|
||||
|
||||
Callable<DefaultDomain> callable;
|
||||
if (args.hasFlag('a')) {
|
||||
region.getOwners().removeAll();
|
||||
|
||||
future = Futures.immediateFuture(null);
|
||||
callable = region::getOwners;
|
||||
} else {
|
||||
if (args.argsLength() < 2) {
|
||||
throw new CommandException("List some names to remove, or use -a to remove all.");
|
||||
@ -220,16 +207,15 @@ public void removeOwner(CommandContext args, Actor sender) throws CommandExcepti
|
||||
WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0));
|
||||
resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME);
|
||||
|
||||
// Then remove it from the owners
|
||||
future = Futures.transform(
|
||||
WorldGuard.getInstance().getExecutorService().submit(resolver),
|
||||
resolver.createRemoveAllFunction(region.getOwners()));
|
||||
callable = resolver;
|
||||
}
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.formatUsing(region.getId(), world.getName())
|
||||
.registerWithSupervisor("Removing owners from the region '%s' on '%s'")
|
||||
final String description = String.format("Removing owners from the region '%s' on '%s'", region.getId(), world.getName());
|
||||
AsyncCommandBuilder.wrap(callable, sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), description)
|
||||
.sendMessageAfterDelay("(Please wait... querying player names...)")
|
||||
.thenRespondWith("Region '%s' updated with owners removed.", "Failed to remove owners");
|
||||
.onSuccess(String.format("Region '%s' updated with owners removed.", region.getId()), region.getOwners()::removeAll)
|
||||
.onFailure("Failed to remove owners", worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
}
|
||||
}
|
||||
|
@ -22,22 +22,19 @@
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.sk89q.minecraft.util.commands.Command;
|
||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
|
||||
import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.command.util.AsyncCommandHelper;
|
||||
import com.sk89q.worldedit.command.util.FutureProgressListener;
|
||||
import com.sk89q.worldedit.command.util.MessageFutureCallback.Builder;
|
||||
import com.sk89q.worldedit.command.util.AsyncCommandBuilder;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.extension.platform.Capability;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.util.formatting.component.ErrorFormat;
|
||||
import com.sk89q.worldedit.util.formatting.component.LabelFormat;
|
||||
import com.sk89q.worldedit.util.formatting.component.SubtleFormat;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
|
||||
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
|
||||
@ -49,7 +46,7 @@
|
||||
import com.sk89q.worldguard.commands.CommandUtils;
|
||||
import com.sk89q.worldguard.commands.task.RegionAdder;
|
||||
import com.sk89q.worldguard.commands.task.RegionLister;
|
||||
import com.sk89q.worldguard.commands.task.RegionManagerReloader;
|
||||
import com.sk89q.worldguard.commands.task.RegionManagerLoader;
|
||||
import com.sk89q.worldguard.commands.task.RegionManagerSaver;
|
||||
import com.sk89q.worldguard.commands.task.RegionRemover;
|
||||
import com.sk89q.worldguard.config.ConfigurationManager;
|
||||
@ -84,6 +81,7 @@
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implements the /region commands for WorldGuard.
|
||||
@ -137,20 +135,18 @@ public void define(CommandContext args, Actor sender) throws CommandException {
|
||||
|
||||
RegionAdder task = new RegionAdder(manager, region);
|
||||
task.addOwnersFromCommand(args, 2);
|
||||
ListenableFuture<?> future = WorldGuard.getInstance().getExecutorService().submit(task);
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), player, worldGuard.getExceptionConverter())
|
||||
.formatUsing(id)
|
||||
.registerWithSupervisor("Adding the region '%s'...")
|
||||
.sendMessageAfterDelay("(Please wait... adding '%s'...)")
|
||||
.thenRespondWith(
|
||||
"A new region has been made named '%s'.",
|
||||
"Failed to add the region '%s'");
|
||||
final String description = String.format("Adding region '%s'", region.getId());
|
||||
AsyncCommandBuilder.wrap(task, sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), description)
|
||||
.onSuccess(String.format("A new region has been made named '%s'.", region.getId()), null)
|
||||
.onFailure("Failed to add the region '%s'", worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-defines a region with a new selection.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -189,23 +185,22 @@ public void redefine(CommandContext args, Actor sender) throws CommandException
|
||||
region.copyFrom(existing);
|
||||
|
||||
RegionAdder task = new RegionAdder(manager, region);
|
||||
ListenableFuture<?> future = WorldGuard.getInstance().getExecutorService().submit(task);
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), player, worldGuard.getExceptionConverter())
|
||||
.formatUsing(id)
|
||||
.registerWithSupervisor("Updating the region '%s'...")
|
||||
.sendMessageAfterDelay("(Please wait... updating '%s'...)")
|
||||
.thenRespondWith(
|
||||
"Region '%s' has been updated with a new area.",
|
||||
"Failed to update the region '%s'");
|
||||
final String description = String.format("Updating region '%s'", region.getId());
|
||||
AsyncCommandBuilder.wrap(task, sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), description)
|
||||
.sendMessageAfterDelay("(Please wait... " + description + ")")
|
||||
.onSuccess(String.format("Region '%s' has been updated with a new area.", region.getId()), null)
|
||||
.onFailure(String.format("Failed to update the region '%s'", region.getId()), worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
* Claiming command for users.
|
||||
*
|
||||
*
|
||||
* <p>This command is a joke and it needs to be rewritten. It was contributed
|
||||
* code :(</p>
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -219,7 +214,7 @@ public void claim(CommandContext args, Actor sender) throws CommandException {
|
||||
|
||||
LocalPlayer player = worldGuard.checkPlayer(sender);
|
||||
RegionPermissionModel permModel = getPermissionModel(player);
|
||||
|
||||
|
||||
// Check permissions
|
||||
if (!permModel.mayClaim()) {
|
||||
throw new CommandPermissionsException();
|
||||
@ -290,20 +285,18 @@ public void claim(CommandContext args, Actor sender) throws CommandException {
|
||||
RegionAdder task = new RegionAdder(manager, region);
|
||||
task.setLocatorPolicy(UserLocatorPolicy.UUID_ONLY);
|
||||
task.setOwnersInput(new String[]{player.getName()});
|
||||
ListenableFuture<?> future = worldGuard.getExecutorService().submit(task);
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), player, worldGuard.getExceptionConverter())
|
||||
.formatUsing(id)
|
||||
.registerWithSupervisor("Claiming the region '%s'...")
|
||||
.sendMessageAfterDelay("(Please wait... claiming '%s'...)")
|
||||
.thenRespondWith(
|
||||
"A new region has been claimed named '%s'.",
|
||||
"Failed to claim the region '%s'");
|
||||
final String description = String.format("Claiming region '%s'", id);
|
||||
AsyncCommandBuilder.wrap(task, sender)
|
||||
.registerWithSupervisor(WorldGuard.getInstance().getSupervisor(), description)
|
||||
.sendMessageAfterDelay("(Please wait... " + description + ")")
|
||||
.onSuccess(TextComponent.of(String.format("A new region has been claimed named '%s'.", id)), null)
|
||||
.onFailure("Failed to claim region", WorldGuard.getInstance().getExceptionConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a WorldEdit selection from a region.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -316,7 +309,7 @@ public void select(CommandContext args, Actor sender) throws CommandException {
|
||||
LocalPlayer player = worldGuard.checkPlayer(sender);
|
||||
RegionManager manager = checkRegionManager(player.getWorld());
|
||||
ProtectedRegion existing;
|
||||
|
||||
|
||||
// If no arguments were given, get the region that the player is inside
|
||||
if (args.argsLength() == 0) {
|
||||
existing = checkRegionStandingIn(manager, player, "/rg select %id%");
|
||||
@ -335,7 +328,7 @@ public void select(CommandContext args, Actor sender) throws CommandException {
|
||||
|
||||
/**
|
||||
* Get information about a region.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -359,7 +352,7 @@ public void info(CommandContext args, Actor sender) throws CommandException {
|
||||
if (!(sender instanceof LocalPlayer)) {
|
||||
throw new CommandException("Please specify the region with /region info -w world_name region_name.");
|
||||
}
|
||||
|
||||
|
||||
existing = checkRegionStandingIn(manager, (LocalPlayer) sender, true,
|
||||
"/rg info -w " + world.getName() + " %id%" + (args.hasFlag('u') ? " -u" : "") + (args.hasFlag('s') ? " -s" : ""));
|
||||
} else { // Get region from the ID
|
||||
@ -384,19 +377,18 @@ public void info(CommandContext args, Actor sender) throws CommandException {
|
||||
// Print region information
|
||||
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(world.getName(), existing,
|
||||
args.hasFlag('u') ? null : WorldGuard.getInstance().getProfileCache(), sender);
|
||||
ListenableFuture<?> future = Futures.transform(
|
||||
WorldGuard.getInstance().getExecutorService().submit(printout),
|
||||
CommandUtils.messageComponentFunction(sender)::apply);
|
||||
|
||||
AsyncCommandHelper.wrap(future, WorldGuard.getInstance().getSupervisor(), sender, WorldGuard.getInstance().getExceptionConverter())
|
||||
.registerWithSupervisor("Fetching region info")
|
||||
AsyncCommandBuilder.wrap(printout, sender)
|
||||
.registerWithSupervisor(WorldGuard.getInstance().getSupervisor(), "Fetching region info")
|
||||
.sendMessageAfterDelay("(Please wait... fetching region information...)")
|
||||
.thenTellErrorsOnly("Failed to fetch region information");
|
||||
.onSuccess((Component) null, sender::print)
|
||||
.onFailure("Failed to fetch region information", WorldGuard.getInstance().getExceptionConverter())
|
||||
.buildAndExec(WorldGuard.getInstance().getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
* List regions.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -441,17 +433,16 @@ public void list(CommandContext args, Actor sender) throws CommandException {
|
||||
task.filterOwnedByName(ownedBy, args.hasFlag('n'));
|
||||
}
|
||||
|
||||
ListenableFuture<?> future = WorldGuard.getInstance().getExecutorService().submit(task);
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.registerWithSupervisor("Getting list of regions...")
|
||||
AsyncCommandBuilder.wrap(task, sender)
|
||||
.registerWithSupervisor(WorldGuard.getInstance().getSupervisor(), "Getting region list")
|
||||
.sendMessageAfterDelay("(Please wait... fetching region list...)")
|
||||
.thenTellErrorsOnly("Failed to fetch region list");
|
||||
.onFailure("Failed to fetch region list", WorldGuard.getInstance().getExceptionConverter())
|
||||
.buildAndExec(WorldGuard.getInstance().getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a flag.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -512,8 +503,6 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
|
||||
|
||||
Collections.sort(flagList);
|
||||
|
||||
// TODO paginationbox + descs etc
|
||||
|
||||
final TextComponent.Builder builder = TextComponent.builder("Available flags: ");
|
||||
|
||||
final HoverEvent clickToSet = HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to set"));
|
||||
@ -530,7 +519,13 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
|
||||
|
||||
sender.printError("Unknown flag specified: " + flagName);
|
||||
sender.print(builder.build());
|
||||
|
||||
if (sender.isPlayer()) {
|
||||
sender.print(TextComponent.of("Or use the command ", TextColor.LIGHT_PURPLE)
|
||||
.append(TextComponent.of("/rg flags " + existing.getId(), TextColor.AQUA)
|
||||
.clickEvent(ClickEvent.of(ClickEvent.Action.RUN_COMMAND,
|
||||
"/rg flags -w " + world.getName() + " " + existing.getId()))));
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (value != null) {
|
||||
if (foundFlag == Flags.BUILD || foundFlag == Flags.BLOCK_BREAK || foundFlag == Flags.BLOCK_PLACE) {
|
||||
@ -562,7 +557,7 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Also make sure that we can use this flag
|
||||
// This permission is confusing and probably should be replaced, but
|
||||
// but not here -- in the model
|
||||
@ -574,7 +569,7 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
|
||||
if (args.hasFlag('g')) {
|
||||
String group = args.getFlag('g');
|
||||
RegionGroupFlag groupFlag = foundFlag.getRegionGroupFlag();
|
||||
|
||||
|
||||
if (groupFlag == null) {
|
||||
throw new CommandException("Region flag '" + foundFlag.getName()
|
||||
+ "' does not have a group flag!");
|
||||
@ -602,7 +597,7 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
|
||||
if (!args.hasFlag('h')) {
|
||||
sender.print("Region flag " + foundFlag.getName() + " set on '" + existing.getId() + "' to '" + value + "'.");
|
||||
}
|
||||
|
||||
|
||||
// No value? Clear the flag, if -g isn't specified
|
||||
} else if (!args.hasFlag('g')) {
|
||||
// Clear the flag only if neither [value] nor [-g group] was given
|
||||
@ -636,17 +631,7 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
|
||||
// Print region information
|
||||
if (args.hasFlag('h')) {
|
||||
int page = args.getFlagInteger('h');
|
||||
final FlagHelperBox flagHelperBox = new FlagHelperBox(world, existing, permModel);
|
||||
flagHelperBox.setComponentsPerPage(18);
|
||||
ListenableFuture<?> future = WorldGuard.getInstance().getExecutorService().submit(() -> {
|
||||
sender.print(flagHelperBox.create(page));
|
||||
return null;
|
||||
});
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.registerWithSupervisor("Getting region flags.")
|
||||
.sendMessageAfterDelay("(Please wait... fetching region info...)")
|
||||
.thenTellErrorsOnly("Failed to fetch region info");
|
||||
sendFlagHelper(sender, world, existing, permModel, page);
|
||||
} else {
|
||||
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(world.getName(), existing, null, sender);
|
||||
printout.append(SubtleFormat.wrap("(Current flags: "));
|
||||
@ -657,39 +642,48 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
|
||||
}
|
||||
|
||||
@Command(aliases = "flags",
|
||||
usage = "<id> [page]",
|
||||
flags = "w:",
|
||||
usage = "[-p <page>] [id]",
|
||||
flags = "p:w:",
|
||||
desc = "View region flags",
|
||||
min = 1, max = 2)
|
||||
min = 0, max = 2)
|
||||
public void flagHelper(CommandContext args, Actor sender) throws CommandException {
|
||||
World world = checkWorld(args, sender, 'w'); // Get the world
|
||||
|
||||
// Lookup the existing region
|
||||
RegionManager manager = checkRegionManager(world);
|
||||
ProtectedRegion region = checkExistingRegion(manager, args.getString(0), true);
|
||||
ProtectedRegion region;
|
||||
if (args.argsLength() == 0) { // Get region from where the player is
|
||||
if (!(sender instanceof LocalPlayer)) {
|
||||
throw new CommandException("Please specify the region with /region info -w world_name region_name.");
|
||||
}
|
||||
|
||||
region = checkRegionStandingIn(manager, (LocalPlayer) sender, true,
|
||||
"/rg flags -w " + world.getName() + " %id%");
|
||||
} else { // Get region from the ID
|
||||
region = checkExistingRegion(manager, args.getString(0), true);
|
||||
}
|
||||
|
||||
final RegionPermissionModel perms = getPermissionModel(sender);
|
||||
if (!perms.mayLookup(region)) {
|
||||
throw new CommandPermissionsException();
|
||||
}
|
||||
int page = args.getInteger(1, 1);
|
||||
int page = args.hasFlag('p') ? args.getFlagInteger('p') : 1;
|
||||
|
||||
sendFlagHelper(sender, world, region, perms, page);
|
||||
}
|
||||
|
||||
private static void sendFlagHelper(Actor sender, World world, ProtectedRegion region, RegionPermissionModel perms, int page) {
|
||||
final FlagHelperBox flagHelperBox = new FlagHelperBox(world, region, perms);
|
||||
flagHelperBox.setComponentsPerPage(18);
|
||||
ListenableFuture<?> future = WorldGuard.getInstance().getExecutorService().submit(() -> {
|
||||
sender.print(flagHelperBox.create(page));
|
||||
return null;
|
||||
});
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.registerWithSupervisor("Getting region flags.")
|
||||
.sendMessageAfterDelay("(Please wait... fetching region info...)")
|
||||
.thenTellErrorsOnly("Failed to fetch region info");
|
||||
AsyncCommandBuilder.wrap(() -> flagHelperBox.create(page), sender)
|
||||
.onSuccess((Component) null, sender::print)
|
||||
.onFailure("Failed to get region flags", WorldGuard.getInstance().getExceptionConverter())
|
||||
.buildAndExec(WorldGuard.getInstance().getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the priority of a region.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -721,7 +715,7 @@ public void setPriority(CommandContext args, Actor sender) throws CommandExcepti
|
||||
|
||||
/**
|
||||
* Set the parent of a region.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -740,7 +734,7 @@ public void setParent(CommandContext args, Actor sender) throws CommandException
|
||||
|
||||
// Lookup the existing region
|
||||
RegionManager manager = checkRegionManager(world);
|
||||
|
||||
|
||||
// Get parent and child
|
||||
child = checkExistingRegion(manager, args.getString(0), false);
|
||||
if (args.argsLength() == 2) {
|
||||
@ -768,7 +762,7 @@ public void setParent(CommandContext args, Actor sender) throws CommandException
|
||||
printout.send(sender);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Tell the user the current inheritance
|
||||
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(world.getName(), child, null, sender);
|
||||
printout.append(LabelFormat.wrap("Inheritance set for region '", child.getId(), "'."));
|
||||
@ -785,7 +779,7 @@ public void setParent(CommandContext args, Actor sender) throws CommandException
|
||||
|
||||
/**
|
||||
* Remove a region.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -821,18 +815,20 @@ public void remove(CommandContext args, Actor sender) throws CommandException {
|
||||
task.setRemovalStrategy(RemovalStrategy.UNSET_PARENT_IN_CHILDREN);
|
||||
}
|
||||
|
||||
AsyncCommandHelper.wrap(WorldGuard.getInstance().getExecutorService().submit(task), worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.formatUsing(existing.getId())
|
||||
.registerWithSupervisor("Removing the region '%s'...")
|
||||
.sendMessageAfterDelay("(Please wait... removing '%s'...)")
|
||||
.thenRespondWith(
|
||||
"The region named '%s' has been removed.",
|
||||
"Failed to remove the region '%s'");
|
||||
final String description = String.format("Removing region '%s' in '%s'", existing.getId(), world.getName());
|
||||
AsyncCommandBuilder.wrap(task, sender)
|
||||
.registerWithSupervisor(WorldGuard.getInstance().getSupervisor(), description)
|
||||
.sendMessageAfterDelay("Please wait... removing region.")
|
||||
.onSuccess((Component) null, removed -> sender.print(TextComponent.of(
|
||||
"Successfully removed " + removed.stream().map(ProtectedRegion::getId).collect(Collectors.joining(", ")) + ".",
|
||||
TextColor.LIGHT_PURPLE)))
|
||||
.onFailure("Failed to remove region", WorldGuard.getInstance().getExceptionConverter())
|
||||
.buildAndExec(WorldGuard.getInstance().getExecutorService());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the region database.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -847,7 +843,7 @@ public void load(CommandContext args, final Actor sender) throws CommandExceptio
|
||||
World world = null;
|
||||
try {
|
||||
world = checkWorld(args, sender, 'w'); // Get the world
|
||||
} catch (CommandException e) {
|
||||
} catch (CommandException ignored) {
|
||||
// assume the user wants to reload all worlds
|
||||
}
|
||||
|
||||
@ -863,10 +859,13 @@ public void load(CommandContext args, final Actor sender) throws CommandExceptio
|
||||
throw new CommandException("No region manager exists for world '" + world.getName() + "'.");
|
||||
}
|
||||
|
||||
ListenableFuture<?> future = WorldGuard.getInstance().getExecutorService().submit(new RegionManagerReloader(manager));
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.forRegionDataLoad(world, false);
|
||||
final String description = String.format("Loading region data for '%s'.", world.getName());
|
||||
AsyncCommandBuilder.wrap(new RegionManagerLoader(manager), sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), description)
|
||||
.sendMessageAfterDelay("Please wait... " + description)
|
||||
.onSuccess(String.format("Loaded region data for '%s'", world.getName()), null)
|
||||
.onFailure(String.format("Failed to load region data for '%s'", world.getName()), worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
} else {
|
||||
// Load regions for all worlds
|
||||
List<RegionManager> managers = new ArrayList<>();
|
||||
@ -878,20 +877,18 @@ public void load(CommandContext args, final Actor sender) throws CommandExceptio
|
||||
}
|
||||
}
|
||||
|
||||
ListenableFuture<?> future = WorldGuard.getInstance().getExecutorService().submit(new RegionManagerReloader(managers));
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.registerWithSupervisor("Loading regions for all worlds")
|
||||
AsyncCommandBuilder.wrap(new RegionManagerLoader(managers), sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), "Loading regions for all worlds")
|
||||
.sendMessageAfterDelay("(Please wait... loading region data for all worlds...)")
|
||||
.thenRespondWith(
|
||||
"Successfully load the region data for all worlds.",
|
||||
"Failed to load regions for all worlds");
|
||||
.onSuccess("Successfully load the region data for all worlds.", null)
|
||||
.onFailure("Failed to load regions for all worlds", worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-save the region database.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -906,7 +903,7 @@ public void save(CommandContext args, final Actor sender) throws CommandExceptio
|
||||
World world = null;
|
||||
try {
|
||||
world = checkWorld(args, sender, 'w'); // Get the world
|
||||
} catch (CommandException e) {
|
||||
} catch (CommandException ignored) {
|
||||
// assume user wants to save all worlds
|
||||
}
|
||||
|
||||
@ -922,35 +919,37 @@ public void save(CommandContext args, final Actor sender) throws CommandExceptio
|
||||
throw new CommandException("No region manager exists for world '" + world.getName() + "'.");
|
||||
}
|
||||
|
||||
ListenableFuture<?> future = WorldGuard.getInstance().getExecutorService().submit(new RegionManagerSaver(manager));
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.forRegionDataSave(world, false);
|
||||
final String description = String.format("Saving region data for '%s'.", world.getName());
|
||||
AsyncCommandBuilder.wrap(new RegionManagerSaver(manager), sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), description)
|
||||
.sendMessageAfterDelay("Please wait... " + description)
|
||||
.onSuccess(String.format("Saving region data for '%s'", world.getName()), null)
|
||||
.onFailure(String.format("Failed to save region data for '%s'", world.getName()), worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
} else {
|
||||
// Save for all worlds
|
||||
List<RegionManager> managers = new ArrayList<>();
|
||||
|
||||
final RegionContainer regionContainer = worldGuard.getPlatform().getRegionContainer();
|
||||
for (World w : WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getWorlds()) {
|
||||
RegionManager manager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(w);
|
||||
RegionManager manager = regionContainer.get(w);
|
||||
if (manager != null) {
|
||||
managers.add(manager);
|
||||
}
|
||||
}
|
||||
|
||||
ListenableFuture<?> future = WorldGuard.getInstance().getExecutorService().submit(new RegionManagerSaver(managers));
|
||||
|
||||
AsyncCommandHelper.wrap(future, worldGuard.getSupervisor(), sender, worldGuard.getExceptionConverter())
|
||||
.registerWithSupervisor("Saving regions for all worlds")
|
||||
AsyncCommandBuilder.wrap(new RegionManagerSaver(managers), sender)
|
||||
.registerWithSupervisor(worldGuard.getSupervisor(), "Saving regions for all worlds")
|
||||
.sendMessageAfterDelay("(Please wait... saving region data for all worlds...)")
|
||||
.thenRespondWith(
|
||||
"Successfully saved the region data for all worlds.",
|
||||
"Failed to save regions for all worlds");
|
||||
.onSuccess("Successfully saved the region data for all worlds.", null)
|
||||
.onFailure("Failed to save regions for all worlds", worldGuard.getExceptionConverter())
|
||||
.buildAndExec(worldGuard.getExecutorService());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the region database.
|
||||
*
|
||||
*
|
||||
* @param args the arguments
|
||||
* @param sender the sender
|
||||
* @throws CommandException any error
|
||||
@ -975,7 +974,7 @@ public void migrateDB(CommandContext args, Actor sender) throws CommandException
|
||||
throw new CommandException("The value of 'to' is not a recognized type of region region data database.");
|
||||
}
|
||||
|
||||
if (from.equals(to)) {
|
||||
if (from == to) {
|
||||
throw new CommandException("It is not possible to migrate between the same types of region data databases.");
|
||||
}
|
||||
|
||||
|
@ -28,16 +28,16 @@
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class RegionManagerReloader implements Callable<Collection<RegionManager>> {
|
||||
public class RegionManagerLoader implements Callable<Collection<RegionManager>> {
|
||||
|
||||
private final Collection<RegionManager> managers;
|
||||
|
||||
public RegionManagerReloader(Collection<RegionManager> managers) {
|
||||
public RegionManagerLoader(Collection<RegionManager> managers) {
|
||||
checkNotNull(managers);
|
||||
this.managers = managers;
|
||||
}
|
||||
|
||||
public RegionManagerReloader(RegionManager... manager) {
|
||||
public RegionManagerLoader(RegionManager... manager) {
|
||||
this(Arrays.asList(manager));
|
||||
}
|
||||
|
@ -139,6 +139,10 @@ public DefaultDomain call() throws UnresolvedNamesException {
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated was only used for Future transformation. Can be replaced with {@code region.getOwners()::addAll} (or getMembers).
|
||||
*/
|
||||
@Deprecated
|
||||
public Function<DefaultDomain, DefaultDomain> createAddAllFunction(final DefaultDomain target) {
|
||||
return domain -> {
|
||||
target.addAll(domain);
|
||||
@ -146,6 +150,10 @@ public Function<DefaultDomain, DefaultDomain> createAddAllFunction(final Default
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated was only used for Future transformation. Can be replaced with {@code region.getOwners()::removeAll} (or getMembers).
|
||||
*/
|
||||
@Deprecated
|
||||
public Function<DefaultDomain, DefaultDomain> createRemoveAllFunction(final DefaultDomain target) {
|
||||
return domain -> {
|
||||
target.removeAll(domain);
|
||||
|
@ -19,16 +19,15 @@
|
||||
|
||||
package com.sk89q.worldguard.util;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.sk89q.minecraft.util.commands.CommandException;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.internal.command.exception.ExceptionConverterHelper;
|
||||
import com.sk89q.worldedit.internal.command.exception.ExceptionMatch;
|
||||
import com.sk89q.worldedit.util.auth.AuthorizationException;
|
||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||
import com.sk89q.worldedit.util.formatting.component.InvalidComponentException;
|
||||
import com.sk89q.worldguard.WorldGuard;
|
||||
import com.sk89q.worldguard.protection.managers.storage.StorageException;
|
||||
import com.sk89q.worldguard.protection.util.UnresolvedNamesException;
|
||||
import org.enginehub.piston.exception.CommandException;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
@ -41,7 +40,7 @@ public class WorldGuardExceptionConverter extends ExceptionConverterHelper {
|
||||
private static final Pattern numberFormat = Pattern.compile("^For input string: \"(.*)\"$");
|
||||
|
||||
private CommandException newCommandException(String message, Throwable cause) {
|
||||
return new CommandException(TextComponent.of(String.valueOf(message)), cause, ImmutableList.of());
|
||||
return new CommandException(message, cause);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
@ -56,6 +55,11 @@ public void convert(NumberFormatException e) throws CommandException {
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(InvalidComponentException e) throws CommandException {
|
||||
throw newCommandException(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ExceptionMatch
|
||||
public void convert(StorageException e) throws CommandException {
|
||||
WorldGuard.logger.log(Level.WARNING, "Error loading/saving regions", e);
|
||||
|
@ -95,9 +95,8 @@ private <T extends Event & CancelLogging> void testEvent(CommandSender receiver,
|
||||
log.info("Event report for " + receiver.getName() + ":\n\n" + result);
|
||||
|
||||
plugin.checkPermission(receiver, "worldguard.debug.pastebin");
|
||||
ActorCallbackPaste
|
||||
.pastebin(WorldGuard.getInstance().getSupervisor(), plugin.wrapCommandSender(receiver), result,
|
||||
"Event debugging report: %s.txt", WorldGuard.getInstance().getExceptionConverter());
|
||||
ActorCallbackPaste.pastebin(WorldGuard.getInstance().getSupervisor(), plugin.wrapCommandSender(receiver),
|
||||
result, "Event debugging report: %s.txt");
|
||||
} else {
|
||||
receiver.sendMessage(result.replaceAll("(?m)^", ChatColor.AQUA.toString()));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user