Add the option to use an asynchronous command execution coordinator

This commit is contained in:
Alexander Söderberg 2020-09-20 17:37:46 +02:00
parent 0ccf8d37e6
commit 9d5f007e37
No known key found for this signature in database
GPG key ID: C0207FF7EA146678
2 changed files with 161 additions and 2 deletions

View file

@ -0,0 +1,156 @@
//
// MIT License
//
// Copyright (c) 2020 Alexander Söderberg
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
package com.intellectualsites.commands.execution;
import com.intellectualsites.commands.Command;
import com.intellectualsites.commands.CommandTree;
import com.intellectualsites.commands.context.CommandContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Execution coordinator parses and/or executes commands on a separate thread from the calling thread
*
* @param <C> Command sender type
*/
public final class AsynchronousCommandExecutionCoordinator<C> extends CommandExecutionCoordinator<C> {
private final Executor executor;
private final boolean synchronizeParsing;
private AsynchronousCommandExecutionCoordinator(@Nullable final Executor executor,
final boolean synchronizeParsing,
@Nonnull final CommandTree<C> commandTree) {
super(commandTree);
this.executor = executor;
this.synchronizeParsing = synchronizeParsing;
}
/**
* Create a new {@link Builder} instance
*
* @param <C> Command sender type
* @return Builder
*/
@Nonnull
public static <C> Builder<C> newBuilder() {
return new Builder<>();
}
@Override
public CompletableFuture<CommandResult<C>> coordinateExecution(@Nonnull final CommandContext<C> commandContext,
@Nonnull final Queue<String> input) {
final Supplier<CommandResult<C>> supplier;
if (this.synchronizeParsing) {
final Optional<Command<C>> commandOptional = this.getCommandTree().parse(commandContext, input);
supplier = () -> {
commandOptional.ifPresent(command -> command.getCommandExecutionHandler().execute(commandContext));
return new CommandResult<>(commandContext);
};
} else {
supplier = () -> {
this.getCommandTree().parse(commandContext, input).ifPresent(
command -> command.getCommandExecutionHandler().execute(commandContext));
return new CommandResult<>(commandContext);
};
}
if (this.executor != null) {
return CompletableFuture.supplyAsync(supplier, this.executor);
} else {
return CompletableFuture.supplyAsync(supplier);
}
}
/**
* Builder for {@link AsynchronousCommandExecutionCoordinator} instances
*
* @param <C> Command sender type
*/
public static final class Builder<C> {
private Executor executor = null;
private boolean synchronizeParsing = false;
private Builder() {
}
/**
* This forces the command parsing to run on the calling thread,
* and only the actual command execution will run using the executor
*
* @return Builder instance
*/
@Nonnull
public Builder<C> withSynchronousParsing() {
this.synchronizeParsing = true;
return this;
}
/**
* Both command parsing and execution will run using the executor
*
* @return Builder instance
*/
@Nonnull
public Builder<C> withAsynchronousParsing() {
this.synchronizeParsing = false;
return this;
}
/**
* Specify an executor that will be used to coordinate tasks.
* By default the executor uses {@link java.util.concurrent.ForkJoinPool#commonPool()}
*
* @param executor Executor to use
* @return Builder instance
*/
@Nonnull
public Builder<C> withExecutor(@Nonnull final Executor executor) {
this.executor = executor;
return this;
}
/**
* Builder a function that generates a command execution coordinator
* using the options specified in this builder
*
* @return Function that builds the coordinator
*/
@Nonnull
public Function<CommandTree<C>, CommandExecutionCoordinator<C>> build() {
return tree -> new AsynchronousCommandExecutionCoordinator<>(this.executor, this.synchronizeParsing, tree);
}
}
}

View file

@ -41,6 +41,7 @@ import com.intellectualsites.commands.bukkit.BukkitCommandManager;
import com.intellectualsites.commands.bukkit.BukkitCommandMetaBuilder; import com.intellectualsites.commands.bukkit.BukkitCommandMetaBuilder;
import com.intellectualsites.commands.bukkit.CloudBukkitCapabilities; import com.intellectualsites.commands.bukkit.CloudBukkitCapabilities;
import com.intellectualsites.commands.bukkit.parsers.WorldArgument; import com.intellectualsites.commands.bukkit.parsers.WorldArgument;
import com.intellectualsites.commands.execution.AsynchronousCommandExecutionCoordinator;
import com.intellectualsites.commands.execution.CommandExecutionCoordinator; import com.intellectualsites.commands.execution.CommandExecutionCoordinator;
import com.intellectualsites.commands.meta.SimpleCommandMeta; import com.intellectualsites.commands.meta.SimpleCommandMeta;
import com.intellectualsites.commands.paper.PaperCommandManager; import com.intellectualsites.commands.paper.PaperCommandManager;
@ -72,10 +73,11 @@ public final class BukkitTest extends JavaPlugin {
@Override @Override
public void onEnable() { public void onEnable() {
try { try {
final Function<CommandTree<CommandSender>, CommandExecutionCoordinator<CommandSender>> executionCoordinatorFunction =
AsynchronousCommandExecutionCoordinator.<CommandSender>newBuilder().build();
mgr = new PaperCommandManager<>( mgr = new PaperCommandManager<>(
this, this,
CommandExecutionCoordinator executionCoordinatorFunction,
.simpleCoordinator(),
Function.identity(), Function.identity(),
Function.identity() Function.identity()
); );
@ -191,6 +193,7 @@ public final class BukkitTest extends JavaPlugin {
} }
Bukkit.broadcastMessage(ChatColor.GRAY + "Using Registration Manager: " Bukkit.broadcastMessage(ChatColor.GRAY + "Using Registration Manager: "
+ this.mgr.getCommandRegistrationHandler().getClass().getSimpleName()); + this.mgr.getCommandRegistrationHandler().getClass().getSimpleName());
Bukkit.broadcastMessage(ChatColor.GRAY + "Calling Thread: " + Thread.currentThread().getName());
} }
@Nonnull @Nonnull