Skip to content

Commit

Permalink
bukkit: parse selectors in correct thread context
Browse files Browse the repository at this point in the history
  • Loading branch information
jpenilla committed Dec 24, 2023
1 parent 4997821 commit 4ec72e2
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import cloud.commandframework.keys.CloudKey;
import io.leangen.geantyref.TypeToken;
import java.util.Set;
import java.util.concurrent.Executor;
import org.apiguardian.api.API;
import org.bukkit.command.CommandSender;

/**
Expand Down Expand Up @@ -56,6 +58,15 @@ public final class BukkitCommandContextKeys {
}
);

/**
* Key used to store an {@link Executor} for the command sender's scheduler.
*
* @since 2.0.0
*/
@API(status = API.Status.STABLE, since = "2.0.0")
public static final CloudKey<Executor> SENDER_SCHEDULER_EXECUTOR = CloudKey.of(
"SenderSchedulerExecutor", Executor.class);

private BukkitCommandContextKeys() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
import cloud.commandframework.bukkit.internal.BukkitBackwardsBrigadierSenderMapper;
import cloud.commandframework.execution.preprocessor.CommandPreprocessingContext;
import cloud.commandframework.execution.preprocessor.CommandPreprocessor;
import java.util.concurrent.Executor;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

Expand Down Expand Up @@ -76,5 +79,22 @@ public void accept(final @NonNull CommandPreprocessingContext<C> context) {
BukkitCommandContextKeys.CLOUD_BUKKIT_CAPABILITIES,
this.commandManager.queryCapabilities()
);

// Store if PaperCommandManager's preprocessor didn't already
if (!context.getCommandContext().contains(BukkitCommandContextKeys.SENDER_SCHEDULER_EXECUTOR)) {
context.getCommandContext().store(BukkitCommandContextKeys.SENDER_SCHEDULER_EXECUTOR, this.mainThreadExecutor());
}
}

private Executor mainThreadExecutor() {
final Plugin plugin = this.commandManager.getOwningPlugin();
final Server server = plugin.getServer();
return task -> {
if (server.isPrimaryThread()) {
task.run();
return;
}
server.getScheduler().runTask(plugin, task);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ private SelectorUtils() {
if (CraftBukkitReflection.MAJOR_REVISION < 13) {
return null;
}
final ArgumentParser<C, Object> wrappedBrigParser = new WrappedBrigadierParser<>(
final WrappedBrigadierParser<C, Object> wrappedBrigParser = new WrappedBrigadierParser<>(
() -> createEntityArgument(single, playersOnly),
ArgumentParser.DEFAULT_ARGUMENT_COUNT,
EntityArgumentParseFunction.INSTANCE
Expand Down Expand Up @@ -267,39 +267,43 @@ protected PlayerSelectorParser(final boolean single) {

private static class ModernSelectorParser<C, T> implements ArgumentParser.FutureArgumentParser<C, T>, SuggestionProvider<C> {

private final ArgumentParser<C, Object> wrappedBrigadierParser;
private final WrappedBrigadierParser<C, Object> wrappedBrigadierParser;
private final SelectorMapper<T> mapper;

ModernSelectorParser(
final ArgumentParser<C, Object> wrapperBrigParser,
final WrappedBrigadierParser<C, Object> wrapperBrigParser,
final SelectorMapper<T> mapper
) {
this.wrappedBrigadierParser = wrapperBrigParser;
this.mapper = mapper;
}

@Override
@SuppressWarnings("unchecked")
public CompletableFuture<ArgumentParseResult<T>> parseFuture(
final CommandContext<C> commandContext,
final CommandInput commandInput
) {
final CommandInput originalCommandInput = commandInput.copy();
return this.wrappedBrigadierParser.parseFuture(commandContext, commandInput)
.thenCompose(result -> {
try {
final String input = originalCommandInput.difference(commandInput);
return ArgumentParseResult.successFuture(
this.mapper.mapResult(input, new EntitySelectorWrapper(commandContext, result))
);
} catch (final CommandSyntaxException ex) {
commandInput.cursor(originalCommandInput.cursor());
return ArgumentParseResult.failureFuture(ex);
} catch (final Exception ex) {
final CompletableFuture<ArgumentParseResult<T>> future = new CompletableFuture<>();
future.completeExceptionally(ex);
return future;
}
});
return CompletableFuture.supplyAsync(() -> {
final CommandInput originalCommandInput = commandInput.copy();
final ArgumentParseResult<Object> result = this.wrappedBrigadierParser.parse(
commandContext,
commandInput
);
if (result.failure().isPresent()) {
return (ArgumentParseResult<T>) result;
}
final String input = originalCommandInput.difference(commandInput);
try {
return ArgumentParseResult.success(
this.mapper.mapResult(input, new EntitySelectorWrapper(commandContext, result.parsedValue().get()))
);
} catch (final CommandSyntaxException ex) {
return ArgumentParseResult.failure(ex);
} catch (final Exception ex) {
throw rethrow(ex);
}
}, commandContext.get(BukkitCommandContextKeys.SENDER_SCHEDULER_EXECUTOR));
}

@Override
Expand Down
4 changes: 4 additions & 0 deletions cloud-minecraft/cloud-paper/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ plugins {
id("cloud.base-conventions")
}

java {
disableAutoTargetJvm()
}

dependencies {
api(projects.cloudBukkit)
compileOnly(libs.paperApi)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ public PaperCommandManager(
final @NonNull Function<C, CommandSender> backwardsCommandSenderMapper
) throws Exception {
super(owningPlugin, commandExecutionCoordinator, commandSenderMapper, backwardsCommandSenderMapper);

this.registerCommandPreProcessor(new PaperCommandPreprocessor<>(this));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// MIT License
//
// Copyright (c) 2022 Alexander Söderberg & Contributors
//
// 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 cloud.commandframework.paper;

import cloud.commandframework.bukkit.BukkitCommandContextKeys;
import cloud.commandframework.bukkit.internal.CraftBukkitReflection;
import cloud.commandframework.execution.preprocessor.CommandPreprocessingContext;
import cloud.commandframework.execution.preprocessor.CommandPreprocessor;
import java.util.concurrent.Executor;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;

@DefaultQualifier(NonNull.class)
final class PaperCommandPreprocessor<C> implements CommandPreprocessor<C> {

private static final boolean FOLIA =
CraftBukkitReflection.classExists("io.papermc.paper.threadedregions.RegionizedServer");

private final PaperCommandManager<C> manager;

PaperCommandPreprocessor(final PaperCommandManager<C> manager) {
this.manager = manager;
}

@Override
public void accept(final CommandPreprocessingContext<C> ctx) {
// cloud-bukkit's preprocessor will store the main thread executor if we don't store anything.
if (FOLIA) {
ctx.getCommandContext().store(
BukkitCommandContextKeys.SENDER_SCHEDULER_EXECUTOR,
this.foliaExecutorFor(ctx.getCommandContext().sender())
);
}
}

private Executor foliaExecutorFor(final C sender) {
final CommandSender commandSender = this.manager.getBackwardsCommandSenderMapper().apply(sender);
final Plugin plugin = this.manager.getOwningPlugin();
if (commandSender instanceof Entity) {
return task -> {
((Entity) commandSender).getScheduler().run(
plugin,
handle -> task.run(),
null
);
};
} else if (commandSender instanceof BlockCommandSender) {
final BlockCommandSender blockSender = (BlockCommandSender) commandSender;
return task -> {
blockSender.getServer().getRegionScheduler().run(
plugin,
blockSender.getBlock().getLocation(),
handle -> task.run()
);
};
}
return task -> {
plugin.getServer().getGlobalRegionScheduler().run(
plugin,
handle -> task.run()
);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ public void registerFeature(
.handler(commandContext -> {
final Player player = commandContext.sender();
final SingleEntitySelector singleEntitySelector = commandContext.get("entity");
singleEntitySelector.single().teleport(player);
player.getServer().getScheduler().runTask(examplePlugin, () -> {
singleEntitySelector.single().teleport(player);
});
player.sendMessage(ChatColor.GREEN + "The entity was teleported to you!");
}));
}
Expand Down
6 changes: 3 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ bungeecord = "1.8-SNAPSHOT"
cloudburst = "1.0.0-SNAPSHOT"
adventureApi = "4.14.0"
adventurePlatform = "4.3.1"
paperApi = "1.16.5-R0.1-SNAPSHOT"
paperApi = "1.20.4-R0.1-SNAPSHOT"
velocityApi = "3.1.1"
spongeApi7 = "7.3.0"
jetbrainsAnnotations = "24.1.0"
Expand Down Expand Up @@ -102,8 +102,8 @@ adventureApi = { group = "net.kyori", name = "adventure-api", version.ref = "adv
adventurePlatformBukkit = { group = "net.kyori", name = "adventure-platform-bukkit", version.ref = "adventurePlatform" }
adventurePlatformBungeecord = { group = "net.kyori", name = "adventure-platform-bungeecord", version.ref = "adventurePlatform" }
adventureTextSerializerPlain = { group = "net.kyori", name = "adventure-text-serializer-plain", version.ref = "adventureApi" }
paperApi = { group = "com.destroystokyo.paper", name = "paper-api", version.ref = "paperApi" }
paperMojangApi = { group = "com.destroystokyo.paper", name = "paper-mojangapi", version.ref = "paperApi" }
paperApi = { group = "io.papermc.paper", name = "paper-api", version.ref = "paperApi" }
paperMojangApi = { group = "io.papermc.paper", name = "paper-mojangapi", version.ref = "paperApi" }
spongeApi7 = { group = "org.spongepowered", name = "spongeapi", version.ref = "spongeApi7" }
velocityApi = { group = "com.velocitypowered", name = "velocity-api", version.ref = "velocityApi" }
fabricMinecraft = { group = "com.mojang", name = "minecraft", version.ref = "fabricMinecraft" }
Expand Down

0 comments on commit 4ec72e2

Please sign in to comment.