mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-28 07:20:24 +01:00
Brigadier based command API
== AT == public net.minecraft.commands.arguments.blocks.BlockInput tag public net.minecraft.commands.arguments.DimensionArgument ERROR_INVALID_VALUE public net.minecraft.server.ReloadableServerResources registryLookup public net.minecraft.server.ReloadableServerResources Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com> Co-authored-by: Marc Baloup <marc.baloup@laposte.net>
This commit is contained in:
parent
aabe9f5264
commit
977543c545
37 changed files with 2324 additions and 304 deletions
|
@ -32,6 +32,24 @@
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -451,7 +459,7 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSmartUsage(final CommandNode<S> node, final S source, final boolean optional, final boolean deep) {
|
||||||
|
- if (!node.canUse(source)) {
|
||||||
|
+ if (source != null && !node.canUse(source)) { // Paper
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -465,7 +473,7 @@
|
||||||
|
final String redirect = node.getRedirect() == this.root ? "..." : "-> " + node.getRedirect().getUsageText();
|
||||||
|
return self + CommandDispatcher.ARGUMENT_SEPARATOR + redirect;
|
||||||
|
} else {
|
||||||
|
- final Collection<CommandNode<S>> children = node.getChildren().stream().filter(c -> c.canUse(source)).collect(Collectors.toList());
|
||||||
|
+ final Collection<CommandNode<S>> children = node.getChildren().stream().filter(c -> source == null || c.canUse(source)).collect(Collectors.toList()); // Paper
|
||||||
|
if (children.size() == 1) {
|
||||||
|
final String usage = this.getSmartUsage(children.iterator().next(), source, childOptional, childOptional);
|
||||||
|
if (usage != null) {
|
||||||
@@ -537,10 +545,14 @@
|
@@ -537,10 +545,14 @@
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (final CommandNode<S> node : parent.getChildren()) {
|
for (final CommandNode<S> node : parent.getChildren()) {
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
|
|
||||||
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
||||||
private final Map<String, CommandNode<S>> children = new LinkedHashMap<>();
|
private final Map<String, CommandNode<S>> children = new LinkedHashMap<>();
|
||||||
@@ -32,6 +34,14 @@
|
@@ -32,6 +34,16 @@
|
||||||
private final RedirectModifier<S> modifier;
|
private final RedirectModifier<S> modifier;
|
||||||
private final boolean forks;
|
private final boolean forks;
|
||||||
private Command<S> command;
|
private Command<S> command;
|
||||||
+ public CommandNode<CommandSourceStack> clientNode; // Paper - Brigadier API
|
+ public CommandNode<CommandSourceStack> clientNode; // Paper - Brigadier API
|
||||||
|
+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> unwrappedCached = null; // Paper - Brigadier Command API
|
||||||
|
+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> wrappedCached = null; // Paper - Brigadier Command API
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ public void removeCommand(String name) {
|
+ public void removeCommand(String name) {
|
||||||
+ this.children.remove(name);
|
+ this.children.remove(name);
|
||||||
|
@ -31,7 +33,7 @@
|
||||||
|
|
||||||
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
protected CommandNode(final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
|
||||||
this.command = command;
|
this.command = command;
|
||||||
@@ -61,7 +71,17 @@
|
@@ -61,7 +73,17 @@
|
||||||
return this.modifier;
|
return this.modifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,3 +52,15 @@
|
||||||
return this.requirement.test(source);
|
return this.requirement.test(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -183,4 +205,11 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Collection<String> getExamples();
|
||||||
|
+ // Paper start - Brigadier Command API
|
||||||
|
+ public void clearAll() {
|
||||||
|
+ this.children.clear();
|
||||||
|
+ this.literals.clear();
|
||||||
|
+ this.arguments.clear();
|
||||||
|
+ }
|
||||||
|
+ // Paper end - Brigadier Command API
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ @Override
|
+ @Override
|
||||||
+ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) {
|
+ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) {
|
||||||
+ throw new UnsupportedOperationException("Not supported yet.");
|
+ return io.papermc.paper.brigadier.NullCommandSender.INSTANCE; // Paper - expose a no-op CommandSender
|
||||||
+ }
|
+ }
|
||||||
+ // CraftBukkit end
|
+ // CraftBukkit end
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
--- a/net/minecraft/commands/CommandSourceStack.java
|
--- a/net/minecraft/commands/CommandSourceStack.java
|
||||||
+++ b/net/minecraft/commands/CommandSourceStack.java
|
+++ b/net/minecraft/commands/CommandSourceStack.java
|
||||||
@@ -45,8 +45,9 @@
|
@@ -45,9 +45,9 @@
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.phys.Vec2;
|
import net.minecraft.world.phys.Vec2;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
+import com.mojang.brigadier.tree.CommandNode; // CraftBukkit
|
+import com.mojang.brigadier.tree.CommandNode; // CraftBukkit
|
||||||
|
|
||||||
-public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider {
|
-public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider {
|
||||||
+public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource { // Paper - Brigadier API
|
-
|
||||||
|
+public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider, io.papermc.paper.command.brigadier.PaperCommandSourceStack { // Paper - Brigadier API
|
||||||
public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player"));
|
public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player"));
|
||||||
public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity"));
|
public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity"));
|
||||||
@@ -65,6 +66,8 @@
|
public final CommandSource source;
|
||||||
|
@@ -65,6 +65,8 @@
|
||||||
private final Vec2 rotation;
|
private final Vec2 rotation;
|
||||||
private final CommandSigningContext signingContext;
|
private final CommandSigningContext signingContext;
|
||||||
private final TaskChainer chatMessageChainer;
|
private final TaskChainer chatMessageChainer;
|
||||||
|
@ -20,31 +21,9 @@
|
||||||
|
|
||||||
public CommandSourceStack(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String name, Component displayName, MinecraftServer server, @Nullable Entity entity) {
|
public CommandSourceStack(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String name, Component displayName, MinecraftServer server, @Nullable Entity entity) {
|
||||||
this(output, pos, rot, world, level, name, displayName, server, entity, false, CommandResultCallback.EMPTY, EntityAnchorArgument.Anchor.FEET, CommandSigningContext.ANONYMOUS, TaskChainer.immediate(server));
|
this(output, pos, rot, world, level, name, displayName, server, entity, false, CommandResultCallback.EMPTY, EntityAnchorArgument.Anchor.FEET, CommandSigningContext.ANONYMOUS, TaskChainer.immediate(server));
|
||||||
@@ -169,11 +172,66 @@
|
@@ -171,8 +173,43 @@
|
||||||
return this.textName;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ // Paper start - Brigadier API
|
|
||||||
@Override
|
@Override
|
||||||
+ public org.bukkit.entity.Entity getBukkitEntity() {
|
|
||||||
+ return getEntity() != null ? getEntity().getBukkitEntity() : null;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
+ public org.bukkit.World getBukkitWorld() {
|
|
||||||
+ return getLevel() != null ? getLevel().getWorld() : null;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
+ public org.bukkit.Location getBukkitLocation() {
|
|
||||||
+ Vec3 pos = getPosition();
|
|
||||||
+ org.bukkit.World world = getBukkitWorld();
|
|
||||||
+ Vec2 rot = getRotation();
|
|
||||||
+ return world != null && pos != null ? new org.bukkit.Location(world, pos.x, pos.y, pos.z, rot != null ? rot.y : 0, rot != null ? rot.x : 0) : null;
|
|
||||||
+ }
|
|
||||||
+ // Paper end - Brigadier API
|
|
||||||
+
|
|
||||||
+ @Override
|
|
||||||
public boolean hasPermission(int level) {
|
public boolean hasPermission(int level) {
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ // Paper start - Thread Safe Vanilla Command permission checking
|
+ // Paper start - Thread Safe Vanilla Command permission checking
|
||||||
|
@ -56,8 +35,8 @@
|
||||||
+ // CraftBukkit end
|
+ // CraftBukkit end
|
||||||
+
|
+
|
||||||
return this.permissionLevel >= level;
|
return this.permissionLevel >= level;
|
||||||
}
|
+ }
|
||||||
|
+
|
||||||
+ // Paper start - Fix permission levels for command blocks
|
+ // Paper start - Fix permission levels for command blocks
|
||||||
+ private boolean forceRespectPermissionLevel() {
|
+ private boolean forceRespectPermissionLevel() {
|
||||||
+ return this.source == CommandSource.NULL || (this.source instanceof final net.minecraft.world.level.BaseCommandBlock commandBlock && commandBlock.getLevel().paperConfig().commandBlocks.forceFollowPermLevel);
|
+ return this.source == CommandSource.NULL || (this.source instanceof final net.minecraft.world.level.BaseCommandBlock commandBlock && commandBlock.getLevel().paperConfig().commandBlocks.forceFollowPermLevel);
|
||||||
|
@ -81,13 +60,12 @@
|
||||||
+ }
|
+ }
|
||||||
+ return hasBukkitPerm.getAsBoolean();
|
+ return hasBukkitPerm.getAsBoolean();
|
||||||
+ // Paper end - Fix permission levels for command blocks
|
+ // Paper end - Fix permission levels for command blocks
|
||||||
+ }
|
}
|
||||||
+ // CraftBukkit end
|
+ // CraftBukkit end
|
||||||
+
|
|
||||||
public Vec3 getPosition() {
|
public Vec3 getPosition() {
|
||||||
return this.worldPosition;
|
return this.worldPosition;
|
||||||
}
|
@@ -302,21 +339,26 @@
|
||||||
@@ -302,21 +360,26 @@
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
|
||||||
|
|
||||||
|
@ -117,11 +95,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -400,4 +463,10 @@
|
@@ -400,4 +442,16 @@
|
||||||
public boolean isSilent() {
|
public boolean isSilent() {
|
||||||
return this.silent;
|
return this.silent;
|
||||||
}
|
}
|
||||||
+
|
+
|
||||||
|
+ // Paper start
|
||||||
|
+ @Override
|
||||||
|
+ public CommandSourceStack getHandle() {
|
||||||
|
+ return this;
|
||||||
|
+ }
|
||||||
|
+ // Paper end
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ public org.bukkit.command.CommandSender getBukkitSender() {
|
+ public org.bukkit.command.CommandSender getBukkitSender() {
|
||||||
+ return this.source.getBukkitSender(this);
|
+ return this.source.getBukkitSender(this);
|
||||||
|
|
|
@ -19,30 +19,45 @@
|
||||||
private final com.mojang.brigadier.CommandDispatcher<CommandSourceStack> dispatcher = new com.mojang.brigadier.CommandDispatcher();
|
private final com.mojang.brigadier.CommandDispatcher<CommandSourceStack> dispatcher = new com.mojang.brigadier.CommandDispatcher();
|
||||||
|
|
||||||
public Commands(Commands.CommandSelection environment, CommandBuildContext commandRegistryAccess) {
|
public Commands(Commands.CommandSelection environment, CommandBuildContext commandRegistryAccess) {
|
||||||
+ this(); // CraftBukkit
|
+ // Paper
|
||||||
AdvancementCommands.register(this.dispatcher);
|
AdvancementCommands.register(this.dispatcher);
|
||||||
AttributeCommand.register(this.dispatcher, commandRegistryAccess);
|
AttributeCommand.register(this.dispatcher, commandRegistryAccess);
|
||||||
ExecuteCommand.register(this.dispatcher, commandRegistryAccess);
|
ExecuteCommand.register(this.dispatcher, commandRegistryAccess);
|
||||||
@@ -252,6 +261,18 @@
|
@@ -250,8 +259,33 @@
|
||||||
PublishCommand.register(this.dispatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (environment.includeIntegrated) {
|
||||||
|
PublishCommand.register(this.dispatcher);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
+ // Paper start - Vanilla command permission fixes
|
+ // Paper start - Vanilla command permission fixes
|
||||||
+ for (final CommandNode<CommandSourceStack> node : this.dispatcher.getRoot().getChildren()) {
|
+ for (final CommandNode<CommandSourceStack> node : this.dispatcher.getRoot().getChildren()) {
|
||||||
+ if (node.getRequirement() == com.mojang.brigadier.builder.ArgumentBuilder.<CommandSourceStack>defaultRequirement()) {
|
+ if (node.getRequirement() == com.mojang.brigadier.builder.ArgumentBuilder.<CommandSourceStack>defaultRequirement()) {
|
||||||
+ node.requirement = stack -> stack.source == CommandSource.NULL || stack.getBukkitSender().hasPermission(org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(node));
|
+ node.requirement = stack -> stack.source == CommandSource.NULL || stack.getBukkitSender().hasPermission(org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(node));
|
||||||
+ }
|
+ }
|
||||||
+ }
|
}
|
||||||
+ // Paper end - Vanilla command permission fixes
|
+ // Paper end - Vanilla command permission fixes
|
||||||
+ // CraftBukkit start
|
+ // Paper start - Brigadier Command API
|
||||||
+ }
|
+ // Create legacy minecraft namespace commands
|
||||||
+
|
+ for (final CommandNode<CommandSourceStack> node : new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren())) {
|
||||||
+ public Commands() {
|
+ // The brigadier dispatcher is not able to resolve nested redirects.
|
||||||
+ // CraftBukkkit end
|
+ // E.g. registering the alias minecraft:tp cannot redirect to tp, as tp itself redirects to teleport.
|
||||||
|
+ // Instead, target the first none redirecting node.
|
||||||
|
+ CommandNode<CommandSourceStack> flattenedAliasTarget = node;
|
||||||
|
+ while (flattenedAliasTarget.getRedirect() != null) flattenedAliasTarget = flattenedAliasTarget.getRedirect();
|
||||||
|
|
||||||
|
+ this.dispatcher.register(
|
||||||
|
+ com.mojang.brigadier.builder.LiteralArgumentBuilder.<CommandSourceStack>literal("minecraft:" + node.getName())
|
||||||
|
+ .executes(flattenedAliasTarget.getCommand())
|
||||||
|
+ .requires(flattenedAliasTarget.getRequirement())
|
||||||
|
+ .redirect(flattenedAliasTarget)
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
+ // Paper end - Brigadier Command API
|
||||||
|
+ // Paper - remove public constructor, no longer needed
|
||||||
this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
|
this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,30 +283,69 @@
|
@@ -262,30 +296,75 @@
|
||||||
return new ParseResults(commandcontextbuilder1, parseResults.getReader(), parseResults.getExceptions());
|
return new ParseResults(commandcontextbuilder1, parseResults.getReader(), parseResults.getExceptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,14 +90,14 @@
|
||||||
+
|
+
|
||||||
+ String newCommand = joiner.join(args);
|
+ String newCommand = joiner.join(args);
|
||||||
+ this.performPrefixedCommand(sender, newCommand, newCommand);
|
+ this.performPrefixedCommand(sender, newCommand, newCommand);
|
||||||
}
|
+ }
|
||||||
+ // CraftBukkit end
|
+ // CraftBukkit end
|
||||||
|
+
|
||||||
+ public void performPrefixedCommand(CommandSourceStack source, String command) {
|
+ public void performPrefixedCommand(CommandSourceStack source, String command) {
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ this.performPrefixedCommand(source, command, command);
|
+ this.performPrefixedCommand(source, command, command);
|
||||||
+ }
|
}
|
||||||
+
|
|
||||||
+ public void performPrefixedCommand(CommandSourceStack commandlistenerwrapper, String s, String label) {
|
+ public void performPrefixedCommand(CommandSourceStack commandlistenerwrapper, String s, String label) {
|
||||||
+ s = s.startsWith("/") ? s.substring(1) : s;
|
+ s = s.startsWith("/") ? s.substring(1) : s;
|
||||||
+ this.performCommand(this.dispatcher.parse(s, commandlistenerwrapper), s, label);
|
+ this.performCommand(this.dispatcher.parse(s, commandlistenerwrapper), s, label);
|
||||||
|
@ -95,6 +110,11 @@
|
||||||
+ }
|
+ }
|
||||||
|
|
||||||
+ public void performCommand(ParseResults<CommandSourceStack> parseresults, String s, String label) { // CraftBukkit
|
+ public void performCommand(ParseResults<CommandSourceStack> parseresults, String s, String label) { // CraftBukkit
|
||||||
|
+ // Paper start
|
||||||
|
+ this.performCommand(parseresults, s, label, false);
|
||||||
|
+ }
|
||||||
|
+ public void performCommand(ParseResults<CommandSourceStack> parseresults, String s, String label, boolean throwCommandError) {
|
||||||
|
+ // Paper end
|
||||||
+ CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource();
|
+ CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource();
|
||||||
+
|
+
|
||||||
Profiler.get().push(() -> {
|
Profiler.get().push(() -> {
|
||||||
|
@ -112,16 +132,17 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
|
+ if (throwCommandError) throw exception;
|
||||||
MutableComponent ichatmutablecomponent = Component.literal(exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage());
|
MutableComponent ichatmutablecomponent = Component.literal(exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage());
|
||||||
|
|
||||||
- if (Commands.LOGGER.isDebugEnabled()) {
|
- if (Commands.LOGGER.isDebugEnabled()) {
|
||||||
- Commands.LOGGER.error("Command exception: /{}", command, exception);
|
- Commands.LOGGER.error("Command exception: /{}", command, exception);
|
||||||
|
+ Commands.LOGGER.error("Command exception: /{}", s, exception); // Paper - always show execution exception in console log
|
||||||
+ if (commandlistenerwrapper.getServer().isDebugging() || Commands.LOGGER.isDebugEnabled()) { // Paper - Debugging
|
+ if (commandlistenerwrapper.getServer().isDebugging() || Commands.LOGGER.isDebugEnabled()) { // Paper - Debugging
|
||||||
+ Commands.LOGGER.error("Command exception: /{}", s, exception);
|
|
||||||
StackTraceElement[] astacktraceelement = exception.getStackTrace();
|
StackTraceElement[] astacktraceelement = exception.getStackTrace();
|
||||||
|
|
||||||
for (int i = 0; i < Math.min(astacktraceelement.length, 3); ++i) {
|
for (int i = 0; i < Math.min(astacktraceelement.length, 3); ++i) {
|
||||||
@@ -298,7 +358,7 @@
|
@@ -298,7 +377,7 @@
|
||||||
}));
|
}));
|
||||||
if (SharedConstants.IS_RUNNING_IN_IDE) {
|
if (SharedConstants.IS_RUNNING_IN_IDE) {
|
||||||
commandlistenerwrapper.sendFailure(Component.literal(Util.describeError(exception)));
|
commandlistenerwrapper.sendFailure(Component.literal(Util.describeError(exception)));
|
||||||
|
@ -130,7 +151,7 @@
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
Profiler.get().pop();
|
Profiler.get().pop();
|
||||||
@@ -307,18 +367,22 @@
|
@@ -307,18 +386,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -159,7 +180,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
if (i > 10) {
|
if (i > 10) {
|
||||||
@@ -333,8 +397,18 @@
|
@@ -333,8 +416,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
ichatmutablecomponent.append((Component) Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC));
|
ichatmutablecomponent.append((Component) Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC));
|
||||||
|
@ -179,7 +200,7 @@
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -368,7 +442,7 @@
|
@@ -368,7 +461,7 @@
|
||||||
|
|
||||||
executioncontext1.close();
|
executioncontext1.close();
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -188,7 +209,7 @@
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
callback.accept(executioncontext);
|
callback.accept(executioncontext);
|
||||||
@@ -377,22 +451,89 @@
|
@@ -377,23 +470,121 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendCommands(ServerPlayer player) {
|
public void sendCommands(ServerPlayer player) {
|
||||||
|
@ -221,13 +242,7 @@
|
||||||
+ private void sendAsync(ServerPlayer player, Collection<CommandNode<CommandSourceStack>> dispatcherRootChildren) {
|
+ private void sendAsync(ServerPlayer player, Collection<CommandNode<CommandSourceStack>> dispatcherRootChildren) {
|
||||||
+ // Paper end - Perf: Async command map building
|
+ // Paper end - Perf: Async command map building
|
||||||
+ Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues
|
+ Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues
|
||||||
+ RootCommandNode vanillaRoot = new RootCommandNode();
|
+ // Paper - brigadier API removes the need to fill the map twice
|
||||||
+
|
|
||||||
+ RootCommandNode<CommandSourceStack> vanilla = player.server.vanillaCommandDispatcher.getDispatcher().getRoot();
|
|
||||||
+ map.put(vanilla, vanillaRoot);
|
|
||||||
+ this.fillUsableCommands(vanilla, vanillaRoot, player.createCommandSourceStack(), (Map) map);
|
|
||||||
+
|
|
||||||
+ // Now build the global commands in a second pass
|
|
||||||
RootCommandNode<SharedSuggestionProvider> rootcommandnode = new RootCommandNode();
|
RootCommandNode<SharedSuggestionProvider> rootcommandnode = new RootCommandNode();
|
||||||
|
|
||||||
map.put(this.dispatcher.getRoot(), rootcommandnode);
|
map.put(this.dispatcher.getRoot(), rootcommandnode);
|
||||||
|
@ -265,6 +280,7 @@
|
||||||
- Iterator iterator = tree.getChildren().iterator();
|
- Iterator iterator = tree.getChildren().iterator();
|
||||||
+ // Paper start - Perf: Async command map building; pass copy of children
|
+ // Paper start - Perf: Async command map building; pass copy of children
|
||||||
+ private void fillUsableCommands(Collection<CommandNode<CommandSourceStack>> children, CommandNode<SharedSuggestionProvider> result, CommandSourceStack source, Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> resultNodes) {
|
+ private void fillUsableCommands(Collection<CommandNode<CommandSourceStack>> children, CommandNode<SharedSuggestionProvider> result, CommandSourceStack source, Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> resultNodes) {
|
||||||
|
+ resultNodes.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains( ":" )); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below
|
||||||
+ Iterator iterator = children.iterator();
|
+ Iterator iterator = children.iterator();
|
||||||
+ // Paper end - Perf: Async command map building
|
+ // Paper end - Perf: Async command map building
|
||||||
|
|
||||||
|
@ -280,10 +296,47 @@
|
||||||
if (commandnode2.canUse(source)) {
|
if (commandnode2.canUse(source)) {
|
||||||
- ArgumentBuilder<SharedSuggestionProvider, ?> argumentbuilder = commandnode2.createBuilder();
|
- ArgumentBuilder<SharedSuggestionProvider, ?> argumentbuilder = commandnode2.createBuilder();
|
||||||
+ ArgumentBuilder argumentbuilder = commandnode2.createBuilder(); // CraftBukkit - decompile error
|
+ ArgumentBuilder argumentbuilder = commandnode2.createBuilder(); // CraftBukkit - decompile error
|
||||||
|
+ // Paper start
|
||||||
|
+ /*
|
||||||
|
+ Because of how commands can be yeeted right left and center due to bad bukkit practices
|
||||||
|
+ we need to be able to ensure that ALL commands are registered (even redirects).
|
||||||
|
|
||||||
|
+ What this will do is IF the redirect seems to be "dead" it will create a builder and essentially populate (flatten)
|
||||||
|
+ all the children from the dead redirect to the node.
|
||||||
|
+
|
||||||
|
+ So, if minecraft:msg redirects to msg but the original msg node has been overriden minecraft:msg will now act as msg and will explicilty inherit its children.
|
||||||
|
+
|
||||||
|
+ The only way to fix this is to either:
|
||||||
|
+ - Send EVERYTHING flattened, don't use redirects
|
||||||
|
+ - Don't allow command nodes to be deleted
|
||||||
|
+ - Do this :)
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+ // Is there an invalid command redirect?
|
||||||
|
+ if (argumentbuilder.getRedirect() != null && (CommandNode) resultNodes.get(argumentbuilder.getRedirect()) == null) {
|
||||||
|
+ // Create the argument builder with the same values as the specified node, but with a different literal and populated children
|
||||||
|
+
|
||||||
|
+ CommandNode<CommandSourceStack> redirect = argumentbuilder.getRedirect();
|
||||||
|
+ // Diff copied from LiteralCommand#createBuilder
|
||||||
|
+ final com.mojang.brigadier.builder.LiteralArgumentBuilder<CommandSourceStack> builder = com.mojang.brigadier.builder.LiteralArgumentBuilder.literal(commandnode2.getName());
|
||||||
|
+ builder.requires(redirect.getRequirement());
|
||||||
|
+ // builder.forward(redirect.getRedirect(), redirect.getRedirectModifier(), redirect.isFork()); We don't want to migrate the forward, since it's invalid.
|
||||||
|
+ if (redirect.getCommand() != null) {
|
||||||
|
+ builder.executes(redirect.getCommand());
|
||||||
|
+ }
|
||||||
|
+ // Diff copied from LiteralCommand#createBuilder
|
||||||
|
+ for (CommandNode<CommandSourceStack> child : redirect.getChildren()) {
|
||||||
|
+ builder.then(child);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ argumentbuilder = builder;
|
||||||
|
+ }
|
||||||
|
+ // Paper end
|
||||||
|
+
|
||||||
argumentbuilder.requires((icompletionprovider) -> {
|
argumentbuilder.requires((icompletionprovider) -> {
|
||||||
return true;
|
return true;
|
||||||
@@ -415,12 +556,12 @@
|
});
|
||||||
|
@@ -415,12 +606,12 @@
|
||||||
argumentbuilder.redirect((CommandNode) resultNodes.get(argumentbuilder.getRedirect()));
|
argumentbuilder.redirect((CommandNode) resultNodes.get(argumentbuilder.getRedirect()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +351,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -481,7 +622,7 @@
|
@@ -481,7 +672,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> HolderLookup.RegistryLookup.Delegate<T> createLookup(final HolderLookup.RegistryLookup<T> original) {
|
private <T> HolderLookup.RegistryLookup.Delegate<T> createLookup(final HolderLookup.RegistryLookup<T> original) {
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
--- a/net/minecraft/commands/arguments/MessageArgument.java
|
--- a/net/minecraft/commands/arguments/MessageArgument.java
|
||||||
+++ b/net/minecraft/commands/arguments/MessageArgument.java
|
+++ b/net/minecraft/commands/arguments/MessageArgument.java
|
||||||
@@ -54,17 +54,21 @@
|
@@ -40,6 +40,11 @@
|
||||||
|
|
||||||
|
public static void resolveChatMessage(CommandContext<CommandSourceStack> context, String name, Consumer<PlayerChatMessage> callback) throws CommandSyntaxException {
|
||||||
|
MessageArgument.Message message = context.getArgument(name, MessageArgument.Message.class);
|
||||||
|
+ // Paper start
|
||||||
|
+ resolveChatMessage(message, context, name, callback);
|
||||||
|
+ }
|
||||||
|
+ public static void resolveChatMessage(MessageArgument.Message message, CommandContext<CommandSourceStack> context, String name, Consumer<PlayerChatMessage> callback) throws CommandSyntaxException {
|
||||||
|
+ // Paper end
|
||||||
|
CommandSourceStack commandSourceStack = context.getSource();
|
||||||
|
Component component = message.resolveComponent(commandSourceStack);
|
||||||
|
CommandSigningContext commandSigningContext = commandSourceStack.getSigningContext();
|
||||||
|
@@ -54,17 +59,21 @@
|
||||||
private static void resolveSignedMessage(Consumer<PlayerChatMessage> callback, CommandSourceStack source, PlayerChatMessage message) {
|
private static void resolveSignedMessage(Consumer<PlayerChatMessage> callback, CommandSourceStack source, PlayerChatMessage message) {
|
||||||
MinecraftServer minecraftServer = source.getServer();
|
MinecraftServer minecraftServer = source.getServer();
|
||||||
CompletableFuture<FilteredText> completableFuture = filterPlainText(source, message);
|
CompletableFuture<FilteredText> completableFuture = filterPlainText(source, message);
|
||||||
|
|
|
@ -162,7 +162,7 @@
|
||||||
+ public static int currentTick; // Paper - improve tick loop
|
+ public static int currentTick; // Paper - improve tick loop
|
||||||
+ public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
+ public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
||||||
+ public int autosavePeriod;
|
+ public int autosavePeriod;
|
||||||
+ public Commands vanillaCommandDispatcher;
|
+ // Paper - don't store the vanilla dispatcher
|
||||||
+ private boolean forceTicks;
|
+ private boolean forceTicks;
|
||||||
+ // CraftBukkit end
|
+ // CraftBukkit end
|
||||||
+ // Spigot start
|
+ // Spigot start
|
||||||
|
@ -202,7 +202,7 @@
|
||||||
this.metricsRecorder = InactiveMetricsRecorder.INSTANCE;
|
this.metricsRecorder = InactiveMetricsRecorder.INSTANCE;
|
||||||
this.onMetricsRecordingStopped = (methodprofilerresults) -> {
|
this.onMetricsRecordingStopped = (methodprofilerresults) -> {
|
||||||
this.stopRecordingMetrics();
|
this.stopRecordingMetrics();
|
||||||
@@ -319,36 +373,68 @@
|
@@ -319,36 +373,67 @@
|
||||||
this.scoreboard = new ServerScoreboard(this);
|
this.scoreboard = new ServerScoreboard(this);
|
||||||
this.customBossEvents = new CustomBossEvents();
|
this.customBossEvents = new CustomBossEvents();
|
||||||
this.suppressedExceptions = new SuppressedExceptionCollector();
|
this.suppressedExceptions = new SuppressedExceptionCollector();
|
||||||
|
@ -254,7 +254,6 @@
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ this.options = options;
|
+ this.options = options;
|
||||||
+ this.worldLoader = worldLoader;
|
+ this.worldLoader = worldLoader;
|
||||||
+ this.vanillaCommandDispatcher = worldstem.dataPackResources().commands; // CraftBukkit
|
|
||||||
+ // Paper start - Handled by TerminalConsoleAppender
|
+ // Paper start - Handled by TerminalConsoleAppender
|
||||||
+ // Try to see if we're actually running in a terminal, disable jline if not
|
+ // Try to see if we're actually running in a terminal, disable jline if not
|
||||||
+ /*
|
+ /*
|
||||||
|
@ -286,7 +285,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readScoreboard(DimensionDataStorage persistentStateManager) {
|
private void readScoreboard(DimensionDataStorage persistentStateManager) {
|
||||||
@@ -357,7 +443,7 @@
|
@@ -357,7 +442,7 @@
|
||||||
|
|
||||||
protected abstract boolean initServer() throws IOException;
|
protected abstract boolean initServer() throws IOException;
|
||||||
|
|
||||||
|
@ -295,7 +294,7 @@
|
||||||
if (!JvmProfiler.INSTANCE.isRunning()) {
|
if (!JvmProfiler.INSTANCE.isRunning()) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@@ -365,12 +451,8 @@
|
@@ -365,12 +450,8 @@
|
||||||
boolean flag = false;
|
boolean flag = false;
|
||||||
ProfiledDuration profiledduration = JvmProfiler.INSTANCE.onWorldLoadedStarted();
|
ProfiledDuration profiledduration = JvmProfiler.INSTANCE.onWorldLoadedStarted();
|
||||||
|
|
||||||
|
@ -309,7 +308,7 @@
|
||||||
if (profiledduration != null) {
|
if (profiledduration != null) {
|
||||||
profiledduration.finish(true);
|
profiledduration.finish(true);
|
||||||
}
|
}
|
||||||
@@ -387,23 +469,241 @@
|
@@ -387,23 +468,244 @@
|
||||||
|
|
||||||
protected void forceDifficulty() {}
|
protected void forceDifficulty() {}
|
||||||
|
|
||||||
|
@ -549,6 +548,9 @@
|
||||||
+
|
+
|
||||||
+ this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
|
+ this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
|
||||||
+ if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
|
+ if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
|
||||||
|
+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // Paper - reset invalid state for event fire below
|
||||||
|
+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - call commands event for regular plugins
|
||||||
|
+ ((org.bukkit.craftbukkit.help.SimpleHelpMap) this.server.getHelpMap()).initializeCommands();
|
||||||
+ this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
|
+ this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
|
||||||
+ this.connection.acceptConnections();
|
+ this.connection.acceptConnections();
|
||||||
+ }
|
+ }
|
||||||
|
@ -565,7 +567,7 @@
|
||||||
|
|
||||||
if (!iworlddataserver.isInitialized()) {
|
if (!iworlddataserver.isInitialized()) {
|
||||||
try {
|
try {
|
||||||
@@ -427,30 +727,8 @@
|
@@ -427,30 +729,8 @@
|
||||||
iworlddataserver.setInitialized(true);
|
iworlddataserver.setInitialized(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,7 +599,7 @@
|
||||||
|
|
||||||
private static void setInitialSpawn(ServerLevel world, ServerLevelData worldProperties, boolean bonusChest, boolean debugWorld) {
|
private static void setInitialSpawn(ServerLevel world, ServerLevelData worldProperties, boolean bonusChest, boolean debugWorld) {
|
||||||
if (debugWorld) {
|
if (debugWorld) {
|
||||||
@@ -458,6 +736,21 @@
|
@@ -458,6 +738,21 @@
|
||||||
} else {
|
} else {
|
||||||
ServerChunkCache chunkproviderserver = world.getChunkSource();
|
ServerChunkCache chunkproviderserver = world.getChunkSource();
|
||||||
ChunkPos chunkcoordintpair = new ChunkPos(chunkproviderserver.randomState().sampler().findSpawnPosition());
|
ChunkPos chunkcoordintpair = new ChunkPos(chunkproviderserver.randomState().sampler().findSpawnPosition());
|
||||||
|
@ -619,7 +621,7 @@
|
||||||
int i = chunkproviderserver.getGenerator().getSpawnHeight(world);
|
int i = chunkproviderserver.getGenerator().getSpawnHeight(world);
|
||||||
|
|
||||||
if (i < world.getMinY()) {
|
if (i < world.getMinY()) {
|
||||||
@@ -516,31 +809,36 @@
|
@@ -516,31 +811,36 @@
|
||||||
iworlddataserver.setGameType(GameType.SPECTATOR);
|
iworlddataserver.setGameType(GameType.SPECTATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,7 +669,7 @@
|
||||||
ForcedChunksSavedData forcedchunk = (ForcedChunksSavedData) worldserver1.getDataStorage().get(ForcedChunksSavedData.factory(), "chunks");
|
ForcedChunksSavedData forcedchunk = (ForcedChunksSavedData) worldserver1.getDataStorage().get(ForcedChunksSavedData.factory(), "chunks");
|
||||||
|
|
||||||
if (forcedchunk != null) {
|
if (forcedchunk != null) {
|
||||||
@@ -555,10 +853,17 @@
|
@@ -555,10 +855,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -689,7 +691,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameType getDefaultGameType() {
|
public GameType getDefaultGameType() {
|
||||||
@@ -588,12 +893,16 @@
|
@@ -588,12 +895,16 @@
|
||||||
worldserver.save((ProgressListener) null, flush, worldserver.noSave && !force);
|
worldserver.save((ProgressListener) null, flush, worldserver.noSave && !force);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -708,7 +710,7 @@
|
||||||
if (flush) {
|
if (flush) {
|
||||||
Iterator iterator1 = this.getAllLevels().iterator();
|
Iterator iterator1 = this.getAllLevels().iterator();
|
||||||
|
|
||||||
@@ -628,18 +937,45 @@
|
@@ -628,18 +939,45 @@
|
||||||
this.stopServer();
|
this.stopServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -755,7 +757,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftServer.LOGGER.info("Saving worlds");
|
MinecraftServer.LOGGER.info("Saving worlds");
|
||||||
@@ -693,6 +1029,15 @@
|
@@ -693,6 +1031,15 @@
|
||||||
} catch (IOException ioexception1) {
|
} catch (IOException ioexception1) {
|
||||||
MinecraftServer.LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), ioexception1);
|
MinecraftServer.LOGGER.error("Failed to unlock level {}", this.storageSource.getLevelId(), ioexception1);
|
||||||
}
|
}
|
||||||
|
@ -771,7 +773,7 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,6 +1054,14 @@
|
@@ -709,6 +1056,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public void halt(boolean waitForShutdown) {
|
public void halt(boolean waitForShutdown) {
|
||||||
|
@ -786,7 +788,7 @@
|
||||||
this.running = false;
|
this.running = false;
|
||||||
if (waitForShutdown) {
|
if (waitForShutdown) {
|
||||||
try {
|
try {
|
||||||
@@ -720,6 +1073,64 @@
|
@@ -720,6 +1075,64 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -851,14 +853,14 @@
|
||||||
protected void runServer() {
|
protected void runServer() {
|
||||||
try {
|
try {
|
||||||
if (!this.initServer()) {
|
if (!this.initServer()) {
|
||||||
@@ -727,9 +1138,26 @@
|
@@ -727,8 +1140,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nextTickTimeNanos = Util.getNanos();
|
this.nextTickTimeNanos = Util.getNanos();
|
||||||
- this.statusIcon = (ServerStatus.Favicon) this.loadStatusIcon().orElse((Object) null);
|
- this.statusIcon = (ServerStatus.Favicon) this.loadStatusIcon().orElse((Object) null);
|
||||||
+ this.statusIcon = (ServerStatus.Favicon) this.loadStatusIcon().orElse(null); // CraftBukkit - decompile error
|
+ this.statusIcon = (ServerStatus.Favicon) this.loadStatusIcon().orElse(null); // CraftBukkit - decompile error
|
||||||
this.status = this.buildServerStatus();
|
this.status = this.buildServerStatus();
|
||||||
|
+
|
||||||
+ // Spigot start
|
+ // Spigot start
|
||||||
+ org.spigotmc.WatchdogThread.hasStarted = true; // Paper
|
+ org.spigotmc.WatchdogThread.hasStarted = true; // Paper
|
||||||
+ Arrays.fill( this.recentTps, 20 );
|
+ Arrays.fill( this.recentTps, 20 );
|
||||||
|
@ -875,11 +877,10 @@
|
||||||
+ LOGGER.info("*************************************************************************************");
|
+ LOGGER.info("*************************************************************************************");
|
||||||
+ }
|
+ }
|
||||||
+ // Paper end - Add onboarding message for initial server start
|
+ // Paper end - Add onboarding message for initial server start
|
||||||
+
|
|
||||||
while (this.running) {
|
while (this.running) {
|
||||||
long i;
|
long i;
|
||||||
|
@@ -744,11 +1174,30 @@
|
||||||
@@ -744,12 +1172,31 @@
|
|
||||||
if (j > MinecraftServer.OVERLOADED_THRESHOLD_NANOS + 20L * i && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= MinecraftServer.OVERLOADED_WARNING_INTERVAL_NANOS + 100L * i) {
|
if (j > MinecraftServer.OVERLOADED_THRESHOLD_NANOS + 20L * i && this.nextTickTimeNanos - this.lastOverloadWarningNanos >= MinecraftServer.OVERLOADED_WARNING_INTERVAL_NANOS + 100L * i) {
|
||||||
long k = j / i;
|
long k = j / i;
|
||||||
|
|
||||||
|
@ -888,7 +889,7 @@
|
||||||
this.nextTickTimeNanos += k * i;
|
this.nextTickTimeNanos += k * i;
|
||||||
this.lastOverloadWarningNanos = this.nextTickTimeNanos;
|
this.lastOverloadWarningNanos = this.nextTickTimeNanos;
|
||||||
}
|
}
|
||||||
}
|
+ }
|
||||||
+ // Spigot start
|
+ // Spigot start
|
||||||
+ // Paper start - further improve server tick loop
|
+ // Paper start - further improve server tick loop
|
||||||
+ currentTime = Util.getNanos();
|
+ currentTime = Util.getNanos();
|
||||||
|
@ -898,20 +899,19 @@
|
||||||
+ tps1.add(currentTps, diff);
|
+ tps1.add(currentTps, diff);
|
||||||
+ tps5.add(currentTps, diff);
|
+ tps5.add(currentTps, diff);
|
||||||
+ tps15.add(currentTps, diff);
|
+ tps15.add(currentTps, diff);
|
||||||
|
+
|
||||||
+ // Backwards compat with bad plugins
|
+ // Backwards compat with bad plugins
|
||||||
+ this.recentTps[0] = tps1.getAverage();
|
+ this.recentTps[0] = tps1.getAverage();
|
||||||
+ this.recentTps[1] = tps5.getAverage();
|
+ this.recentTps[1] = tps5.getAverage();
|
||||||
+ this.recentTps[2] = tps15.getAverage();
|
+ this.recentTps[2] = tps15.getAverage();
|
||||||
+ tickSection = currentTime;
|
+ tickSection = currentTime;
|
||||||
+ }
|
}
|
||||||
+ // Paper end - further improve server tick loop
|
+ // Paper end - further improve server tick loop
|
||||||
+ // Spigot end
|
+ // Spigot end
|
||||||
+
|
|
||||||
boolean flag = i == 0L;
|
boolean flag = i == 0L;
|
||||||
|
|
||||||
if (this.debugCommandProfilerDelayStart) {
|
@@ -757,6 +1206,8 @@
|
||||||
@@ -757,6 +1204,8 @@
|
|
||||||
this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount);
|
this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,7 +920,7 @@
|
||||||
this.nextTickTimeNanos += i;
|
this.nextTickTimeNanos += i;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -830,6 +1279,14 @@
|
@@ -830,6 +1281,14 @@
|
||||||
this.services.profileCache().clearExecutor();
|
this.services.profileCache().clearExecutor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -935,7 +935,7 @@
|
||||||
this.onServerExit();
|
this.onServerExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -889,9 +1346,16 @@
|
@@ -889,9 +1348,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean haveTime() {
|
private boolean haveTime() {
|
||||||
|
@ -953,7 +953,7 @@
|
||||||
public static boolean throwIfFatalException() {
|
public static boolean throwIfFatalException() {
|
||||||
RuntimeException runtimeexception = (RuntimeException) MinecraftServer.fatalException.get();
|
RuntimeException runtimeexception = (RuntimeException) MinecraftServer.fatalException.get();
|
||||||
|
|
||||||
@@ -903,7 +1367,7 @@
|
@@ -903,7 +1369,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setFatalException(RuntimeException exception) {
|
public static void setFatalException(RuntimeException exception) {
|
||||||
|
@ -962,7 +962,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -961,6 +1425,7 @@
|
@@ -961,6 +1427,7 @@
|
||||||
if (super.pollTask()) {
|
if (super.pollTask()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -970,7 +970,7 @@
|
||||||
if (this.tickRateManager.isSprinting() || this.haveTime()) {
|
if (this.tickRateManager.isSprinting() || this.haveTime()) {
|
||||||
Iterator iterator = this.getAllLevels().iterator();
|
Iterator iterator = this.getAllLevels().iterator();
|
||||||
|
|
||||||
@@ -968,16 +1433,16 @@
|
@@ -968,16 +1435,16 @@
|
||||||
ServerLevel worldserver = (ServerLevel) iterator.next();
|
ServerLevel worldserver = (ServerLevel) iterator.next();
|
||||||
|
|
||||||
if (worldserver.getChunkSource().pollTask()) {
|
if (worldserver.getChunkSource().pollTask()) {
|
||||||
|
@ -990,7 +990,7 @@
|
||||||
Profiler.get().incrementCounter("runTask");
|
Profiler.get().incrementCounter("runTask");
|
||||||
super.doRunTask(ticktask);
|
super.doRunTask(ticktask);
|
||||||
}
|
}
|
||||||
@@ -1025,6 +1490,7 @@
|
@@ -1025,6 +1492,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tickServer(BooleanSupplier shouldKeepTicking) {
|
public void tickServer(BooleanSupplier shouldKeepTicking) {
|
||||||
|
@ -998,7 +998,7 @@
|
||||||
long i = Util.getNanos();
|
long i = Util.getNanos();
|
||||||
int j = this.pauseWhileEmptySeconds() * 20;
|
int j = this.pauseWhileEmptySeconds() * 20;
|
||||||
|
|
||||||
@@ -1041,11 +1507,13 @@
|
@@ -1041,11 +1509,13 @@
|
||||||
this.autoSave();
|
this.autoSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,7 +1012,7 @@
|
||||||
++this.tickCount;
|
++this.tickCount;
|
||||||
this.tickRateManager.tick();
|
this.tickRateManager.tick();
|
||||||
this.tickChildren(shouldKeepTicking);
|
this.tickChildren(shouldKeepTicking);
|
||||||
@@ -1055,12 +1523,18 @@
|
@@ -1055,12 +1525,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
--this.ticksUntilAutosave;
|
--this.ticksUntilAutosave;
|
||||||
|
@ -1032,7 +1032,7 @@
|
||||||
gameprofilerfiller.push("tallying");
|
gameprofilerfiller.push("tallying");
|
||||||
long k = Util.getNanos() - i;
|
long k = Util.getNanos() - i;
|
||||||
int l = this.tickCount % 100;
|
int l = this.tickCount % 100;
|
||||||
@@ -1069,12 +1543,17 @@
|
@@ -1069,12 +1545,17 @@
|
||||||
this.aggregatedTickTimesNanos += k;
|
this.aggregatedTickTimesNanos += k;
|
||||||
this.tickTimesNanos[l] = k;
|
this.tickTimesNanos[l] = k;
|
||||||
this.smoothedTickTimeMillis = this.smoothedTickTimeMillis * 0.8F + (float) k / (float) TimeUtil.NANOSECONDS_PER_MILLISECOND * 0.19999999F;
|
this.smoothedTickTimeMillis = this.smoothedTickTimeMillis * 0.8F + (float) k / (float) TimeUtil.NANOSECONDS_PER_MILLISECOND * 0.19999999F;
|
||||||
|
@ -1051,7 +1051,7 @@
|
||||||
MinecraftServer.LOGGER.debug("Autosave started");
|
MinecraftServer.LOGGER.debug("Autosave started");
|
||||||
ProfilerFiller gameprofilerfiller = Profiler.get();
|
ProfilerFiller gameprofilerfiller = Profiler.get();
|
||||||
|
|
||||||
@@ -1123,7 +1602,7 @@
|
@@ -1123,7 +1604,7 @@
|
||||||
private ServerStatus buildServerStatus() {
|
private ServerStatus buildServerStatus() {
|
||||||
ServerStatus.Players serverping_serverpingplayersample = this.buildPlayerStatus();
|
ServerStatus.Players serverping_serverpingplayersample = this.buildPlayerStatus();
|
||||||
|
|
||||||
|
@ -1060,7 +1060,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServerStatus.Players buildPlayerStatus() {
|
private ServerStatus.Players buildPlayerStatus() {
|
||||||
@@ -1133,7 +1612,7 @@
|
@@ -1133,7 +1614,7 @@
|
||||||
if (this.hidesOnlinePlayers()) {
|
if (this.hidesOnlinePlayers()) {
|
||||||
return new ServerStatus.Players(i, list.size(), List.of());
|
return new ServerStatus.Players(i, list.size(), List.of());
|
||||||
} else {
|
} else {
|
||||||
|
@ -1069,7 +1069,7 @@
|
||||||
ObjectArrayList<GameProfile> objectarraylist = new ObjectArrayList(j);
|
ObjectArrayList<GameProfile> objectarraylist = new ObjectArrayList(j);
|
||||||
int k = Mth.nextInt(this.random, 0, list.size() - j);
|
int k = Mth.nextInt(this.random, 0, list.size() - j);
|
||||||
|
|
||||||
@@ -1154,24 +1633,72 @@
|
@@ -1154,24 +1635,72 @@
|
||||||
this.getPlayerList().getPlayers().forEach((entityplayer) -> {
|
this.getPlayerList().getPlayers().forEach((entityplayer) -> {
|
||||||
entityplayer.connection.suspendFlushing();
|
entityplayer.connection.suspendFlushing();
|
||||||
});
|
});
|
||||||
|
@ -1100,7 +1100,7 @@
|
||||||
+ while (!this.processQueue.isEmpty()) {
|
+ while (!this.processQueue.isEmpty()) {
|
||||||
+ this.processQueue.remove().run();
|
+ this.processQueue.remove().run();
|
||||||
+ }
|
+ }
|
||||||
|
+
|
||||||
+ // Send time updates to everyone, it will get the right time from the world the player is in.
|
+ // Send time updates to everyone, it will get the right time from the world the player is in.
|
||||||
+ // Paper start - Perf: Optimize time updates
|
+ // Paper start - Perf: Optimize time updates
|
||||||
+ for (final ServerLevel level : this.getAllLevels()) {
|
+ for (final ServerLevel level : this.getAllLevels()) {
|
||||||
|
@ -1120,7 +1120,7 @@
|
||||||
+ // Paper end - Perf: Optimize time updates
|
+ // Paper end - Perf: Optimize time updates
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
|
||||||
+ this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked
|
+ this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked
|
||||||
+ Iterator iterator = this.getAllLevels().iterator(); // Paper - Throw exception on world create while being ticked; move down
|
+ Iterator iterator = this.getAllLevels().iterator(); // Paper - Throw exception on world create while being ticked; move down
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
|
@ -1143,7 +1143,7 @@
|
||||||
|
|
||||||
gameprofilerfiller.push("tick");
|
gameprofilerfiller.push("tick");
|
||||||
|
|
||||||
@@ -1186,7 +1713,9 @@
|
@@ -1186,7 +1715,9 @@
|
||||||
|
|
||||||
gameprofilerfiller.pop();
|
gameprofilerfiller.pop();
|
||||||
gameprofilerfiller.pop();
|
gameprofilerfiller.pop();
|
||||||
|
@ -1153,7 +1153,7 @@
|
||||||
|
|
||||||
gameprofilerfiller.popPush("connection");
|
gameprofilerfiller.popPush("connection");
|
||||||
this.tickConnection();
|
this.tickConnection();
|
||||||
@@ -1267,6 +1796,22 @@
|
@@ -1267,6 +1798,22 @@
|
||||||
return (ServerLevel) this.levels.get(key);
|
return (ServerLevel) this.levels.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1176,7 +1176,7 @@
|
||||||
public Set<ResourceKey<Level>> levelKeys() {
|
public Set<ResourceKey<Level>> levelKeys() {
|
||||||
return this.levels.keySet();
|
return this.levels.keySet();
|
||||||
}
|
}
|
||||||
@@ -1296,7 +1841,7 @@
|
@@ -1296,7 +1843,7 @@
|
||||||
|
|
||||||
@DontObfuscate
|
@DontObfuscate
|
||||||
public String getServerModName() {
|
public String getServerModName() {
|
||||||
|
@ -1185,7 +1185,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public SystemReport fillSystemReport(SystemReport details) {
|
public SystemReport fillSystemReport(SystemReport details) {
|
||||||
@@ -1347,7 +1892,7 @@
|
@@ -1347,7 +1894,7 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendSystemMessage(Component message) {
|
public void sendSystemMessage(Component message) {
|
||||||
|
@ -1194,7 +1194,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyPair getKeyPair() {
|
public KeyPair getKeyPair() {
|
||||||
@@ -1385,11 +1930,14 @@
|
@@ -1385,11 +1932,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1214,7 +1214,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1403,7 +1951,7 @@
|
@@ -1403,7 +1953,7 @@
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
ServerLevel worldserver = (ServerLevel) iterator.next();
|
ServerLevel worldserver = (ServerLevel) iterator.next();
|
||||||
|
|
||||||
|
@ -1223,7 +1223,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1481,10 +2029,20 @@
|
@@ -1481,10 +2031,20 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMotd() {
|
public String getMotd() {
|
||||||
|
@ -1245,7 +1245,7 @@
|
||||||
this.motd = motd;
|
this.motd = motd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1507,7 +2065,7 @@
|
@@ -1507,7 +2067,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerConnectionListener getConnection() {
|
public ServerConnectionListener getConnection() {
|
||||||
|
@ -1254,7 +1254,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReady() {
|
public boolean isReady() {
|
||||||
@@ -1593,7 +2151,7 @@
|
@@ -1593,7 +2153,7 @@
|
||||||
@Override
|
@Override
|
||||||
public void executeIfPossible(Runnable runnable) {
|
public void executeIfPossible(Runnable runnable) {
|
||||||
if (this.isStopped()) {
|
if (this.isStopped()) {
|
||||||
|
@ -1263,7 +1263,7 @@
|
||||||
} else {
|
} else {
|
||||||
super.executeIfPossible(runnable);
|
super.executeIfPossible(runnable);
|
||||||
}
|
}
|
||||||
@@ -1632,13 +2190,19 @@
|
@@ -1632,13 +2192,19 @@
|
||||||
return this.functionManager;
|
return this.functionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1285,14 +1285,15 @@
|
||||||
}, this).thenCompose((immutablelist) -> {
|
}, this).thenCompose((immutablelist) -> {
|
||||||
MultiPackResourceManager resourcemanager = new MultiPackResourceManager(PackType.SERVER_DATA, immutablelist);
|
MultiPackResourceManager resourcemanager = new MultiPackResourceManager(PackType.SERVER_DATA, immutablelist);
|
||||||
List<Registry.PendingTags<?>> list = TagLoader.loadTagsForExistingRegistries(resourcemanager, this.registries.compositeAccess());
|
List<Registry.PendingTags<?>> list = TagLoader.loadTagsForExistingRegistries(resourcemanager, this.registries.compositeAccess());
|
||||||
@@ -1654,17 +2218,21 @@
|
@@ -1652,6 +2218,7 @@
|
||||||
|
return new MinecraftServer.ReloadableResources(resourcemanager, datapackresources);
|
||||||
|
});
|
||||||
}).thenAcceptAsync((minecraftserver_reloadableresources) -> {
|
}).thenAcceptAsync((minecraftserver_reloadableresources) -> {
|
||||||
|
+ io.papermc.paper.command.brigadier.PaperBrigadier.moveBukkitCommands(this.resources.managers().getCommands(), minecraftserver_reloadableresources.managers().commands); // Paper
|
||||||
this.resources.close();
|
this.resources.close();
|
||||||
this.resources = minecraftserver_reloadableresources;
|
this.resources = minecraftserver_reloadableresources;
|
||||||
+ this.server.syncCommands(); // SPIGOT-5884: Lost on reload
|
|
||||||
this.packRepository.setSelected(dataPacks);
|
this.packRepository.setSelected(dataPacks);
|
||||||
WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures());
|
@@ -1660,11 +2227,23 @@
|
||||||
|
|
||||||
this.worldData.setDataConfiguration(worlddataconfiguration);
|
this.worldData.setDataConfiguration(worlddataconfiguration);
|
||||||
this.resources.managers.updateStaticRegistryTags();
|
this.resources.managers.updateStaticRegistryTags();
|
||||||
this.resources.managers.getRecipeManager().finalizeRecipeLoading(this.worldData.enabledFeatures());
|
this.resources.managers.getRecipeManager().finalizeRecipeLoading(this.worldData.enabledFeatures());
|
||||||
|
@ -1303,11 +1304,20 @@
|
||||||
this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
|
this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
|
||||||
this.fuelValues = FuelValues.vanillaBurnTimes(this.registries.compositeAccess(), this.worldData.enabledFeatures());
|
this.fuelValues = FuelValues.vanillaBurnTimes(this.registries.compositeAccess(), this.worldData.enabledFeatures());
|
||||||
+ org.bukkit.craftbukkit.block.data.CraftBlockData.reloadCache(); // Paper - cache block data strings; they can be defined by datapacks so refresh it here
|
+ org.bukkit.craftbukkit.block.data.CraftBlockData.reloadCache(); // Paper - cache block data strings; they can be defined by datapacks so refresh it here
|
||||||
|
+ // Paper start - brigadier command API
|
||||||
|
+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // reset invalid state for event fire below
|
||||||
|
+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // call commands event for regular plugins
|
||||||
|
+ final org.bukkit.craftbukkit.help.SimpleHelpMap helpMap = (org.bukkit.craftbukkit.help.SimpleHelpMap) this.server.getHelpMap();
|
||||||
|
+ helpMap.clear();
|
||||||
|
+ helpMap.initializeGeneralTopics();
|
||||||
|
+ helpMap.initializeCommands();
|
||||||
|
+ this.server.syncCommands(); // Refresh commands after event
|
||||||
|
+ // Paper end
|
||||||
+ new io.papermc.paper.event.server.ServerResourcesReloadedEvent(cause).callEvent(); // Paper - Add ServerResourcesReloadedEvent; fire after everything has been reloaded
|
+ new io.papermc.paper.event.server.ServerResourcesReloadedEvent(cause).callEvent(); // Paper - Add ServerResourcesReloadedEvent; fire after everything has been reloaded
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
if (this.isSameThread()) {
|
if (this.isSameThread()) {
|
||||||
@@ -1789,14 +2357,15 @@
|
@@ -1789,14 +2368,15 @@
|
||||||
if (this.isEnforceWhitelist()) {
|
if (this.isEnforceWhitelist()) {
|
||||||
PlayerList playerlist = source.getServer().getPlayerList();
|
PlayerList playerlist = source.getServer().getPlayerList();
|
||||||
UserWhiteList whitelist = playerlist.getWhiteList();
|
UserWhiteList whitelist = playerlist.getWhiteList();
|
||||||
|
@ -1325,7 +1335,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1952,7 +2521,7 @@
|
@@ -1952,7 +2532,7 @@
|
||||||
final List<String> list = Lists.newArrayList();
|
final List<String> list = Lists.newArrayList();
|
||||||
final GameRules gamerules = this.getGameRules();
|
final GameRules gamerules = this.getGameRules();
|
||||||
|
|
||||||
|
@ -1334,7 +1344,7 @@
|
||||||
@Override
|
@Override
|
||||||
public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) {
|
public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) {
|
||||||
list.add(String.format(Locale.ROOT, "%s=%s\n", key.getId(), gamerules.getRule(key)));
|
list.add(String.format(Locale.ROOT, "%s=%s\n", key.getId(), gamerules.getRule(key)));
|
||||||
@@ -2058,7 +2627,7 @@
|
@@ -2058,7 +2638,7 @@
|
||||||
try {
|
try {
|
||||||
label51:
|
label51:
|
||||||
{
|
{
|
||||||
|
@ -1343,22 +1353,19 @@
|
||||||
|
|
||||||
try {
|
try {
|
||||||
arraylist = Lists.newArrayList(NativeModuleLister.listModules());
|
arraylist = Lists.newArrayList(NativeModuleLister.listModules());
|
||||||
@@ -2105,9 +2674,24 @@
|
@@ -2108,6 +2688,21 @@
|
||||||
if (bufferedwriter != null) {
|
|
||||||
bufferedwriter.close();
|
}
|
||||||
}
|
|
||||||
+
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ public boolean isDebugging() {
|
+ public boolean isDebugging() {
|
||||||
+ return false;
|
+ return false;
|
||||||
+ }
|
+ }
|
||||||
|
+
|
||||||
+ public static MinecraftServer getServer() {
|
+ public static MinecraftServer getServer() {
|
||||||
+ return SERVER; // Paper
|
+ return SERVER; // Paper
|
||||||
}
|
+ }
|
||||||
|
+
|
||||||
+ @Deprecated
|
+ @Deprecated
|
||||||
+ public static RegistryAccess getDefaultRegistryAccess() {
|
+ public static RegistryAccess getDefaultRegistryAccess() {
|
||||||
+ return CraftRegistry.getMinecraftRegistry();
|
+ return CraftRegistry.getMinecraftRegistry();
|
||||||
|
@ -1368,7 +1375,7 @@
|
||||||
private ProfilerFiller createProfiler() {
|
private ProfilerFiller createProfiler() {
|
||||||
if (this.willStartRecordingMetrics) {
|
if (this.willStartRecordingMetrics) {
|
||||||
this.metricsRecorder = ActiveMetricsRecorder.createStarted(new ServerMetricsSamplersProvider(Util.timeSource, this.isDedicatedServer()), Util.timeSource, Util.ioPool(), new MetricsPersister("server"), this.onMetricsRecordingStopped, (path) -> {
|
this.metricsRecorder = ActiveMetricsRecorder.createStarted(new ServerMetricsSamplersProvider(Util.timeSource, this.isDedicatedServer()), Util.timeSource, Util.ioPool(), new MetricsPersister("server"), this.onMetricsRecordingStopped, (path) -> {
|
||||||
@@ -2225,18 +2809,24 @@
|
@@ -2225,18 +2820,24 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logChatMessage(Component message, ChatType.Bound params, @Nullable String prefix) {
|
public void logChatMessage(Component message, ChatType.Bound params, @Nullable String prefix) {
|
||||||
|
@ -1397,7 +1404,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean logIPs() {
|
public boolean logIPs() {
|
||||||
@@ -2379,4 +2969,30 @@
|
@@ -2379,4 +2980,30 @@
|
||||||
public static record ServerResourcePackInfo(UUID id, String url, String hash, boolean isRequired, @Nullable Component prompt) {
|
public static record ServerResourcePackInfo(UUID id, String url, String hash, boolean isRequired, @Nullable Component prompt) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
--- a/net/minecraft/server/ReloadableServerResources.java
|
||||||
|
+++ b/net/minecraft/server/ReloadableServerResources.java
|
||||||
|
@@ -39,6 +39,7 @@
|
||||||
|
this.postponedTags = pendingTagLoads;
|
||||||
|
this.recipes = new RecipeManager(registries);
|
||||||
|
this.commands = new Commands(environment, CommandBuildContext.simple(registries, enabledFeatures));
|
||||||
|
+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setDispatcher(this.commands, CommandBuildContext.simple(registries, enabledFeatures)); // Paper - Brigadier Command API
|
||||||
|
this.advancements = new ServerAdvancementManager(registries);
|
||||||
|
this.functionLibrary = new ServerFunctionLibrary(functionPermissionLevel, this.commands.getDispatcher());
|
||||||
|
}
|
||||||
|
@@ -83,6 +84,14 @@
|
||||||
|
ReloadableServerResources reloadableServerResources = new ReloadableServerResources(
|
||||||
|
reloadResult.layers(), reloadResult.lookupWithUpdatedTags(), enabledFeatures, environment, pendingTagLoads, functionPermissionLevel
|
||||||
|
);
|
||||||
|
+ // Paper start - call commands event for bootstraps
|
||||||
|
+ //noinspection ConstantValue
|
||||||
|
+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(
|
||||||
|
+ io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS,
|
||||||
|
+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE,
|
||||||
|
+ io.papermc.paper.plugin.bootstrap.BootstrapContext.class,
|
||||||
|
+ MinecraftServer.getServer() == null ? io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL : io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD);
|
||||||
|
+ // Paper end - call commands event
|
||||||
|
return SimpleReloadInstance.create(
|
||||||
|
resourceManager,
|
||||||
|
reloadableServerResources.listeners(),
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
public CommandDispatcher<CommandSourceStack> getDispatcher() {
|
public CommandDispatcher<CommandSourceStack> getDispatcher() {
|
||||||
- return this.server.getCommands().getDispatcher();
|
- return this.server.getCommands().getDispatcher();
|
||||||
+ return this.server.vanillaCommandDispatcher.getDispatcher(); // CraftBukkit
|
+ return this.server.getCommands().getDispatcher(); // CraftBukkit // Paper - Don't override command dispatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tick() {
|
public void tick() {
|
||||||
|
|
|
@ -163,7 +163,7 @@
|
||||||
DedicatedServer.LOGGER.info("Loading properties");
|
DedicatedServer.LOGGER.info("Loading properties");
|
||||||
DedicatedServerProperties dedicatedserverproperties = this.settings.getProperties();
|
DedicatedServerProperties dedicatedserverproperties = this.settings.getProperties();
|
||||||
|
|
||||||
@@ -126,14 +213,50 @@
|
@@ -126,14 +213,49 @@
|
||||||
this.setPreventProxyConnections(dedicatedserverproperties.preventProxyConnections);
|
this.setPreventProxyConnections(dedicatedserverproperties.preventProxyConnections);
|
||||||
this.setLocalIp(dedicatedserverproperties.serverIp);
|
this.setLocalIp(dedicatedserverproperties.serverIp);
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,6 @@
|
||||||
+ io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command
|
+ io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command
|
||||||
+ com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics
|
+ com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics
|
||||||
+ com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
|
+ com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
|
||||||
+ io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // Paper - init PaperBrigadierProvider
|
|
||||||
|
|
||||||
this.setPvpAllowed(dedicatedserverproperties.pvp);
|
this.setPvpAllowed(dedicatedserverproperties.pvp);
|
||||||
this.setFlightAllowed(dedicatedserverproperties.allowFlight);
|
this.setFlightAllowed(dedicatedserverproperties.allowFlight);
|
||||||
|
@ -215,7 +214,7 @@
|
||||||
InetAddress inetaddress = null;
|
InetAddress inetaddress = null;
|
||||||
|
|
||||||
if (!this.getLocalIp().isEmpty()) {
|
if (!this.getLocalIp().isEmpty()) {
|
||||||
@@ -143,34 +266,55 @@
|
@@ -143,34 +265,55 @@
|
||||||
if (this.getPort() < 0) {
|
if (this.getPort() < 0) {
|
||||||
this.setPort(dedicatedserverproperties.serverPort);
|
this.setPort(dedicatedserverproperties.serverPort);
|
||||||
}
|
}
|
||||||
|
@ -277,7 +276,7 @@
|
||||||
this.debugSampleSubscriptionTracker = new DebugSampleSubscriptionTracker(this.getPlayerList());
|
this.debugSampleSubscriptionTracker = new DebugSampleSubscriptionTracker(this.getPlayerList());
|
||||||
this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME);
|
this.tickTimeLogger = new RemoteSampleLogger(TpsDebugDimensions.values().length, this.debugSampleSubscriptionTracker, RemoteDebugSampleType.TICK_TIME);
|
||||||
long i = Util.getNanos();
|
long i = Util.getNanos();
|
||||||
@@ -178,13 +322,13 @@
|
@@ -178,13 +321,13 @@
|
||||||
SkullBlockEntity.setup(this.services, this);
|
SkullBlockEntity.setup(this.services, this);
|
||||||
GameProfileCache.setUsesAuthentication(this.usesAuthentication());
|
GameProfileCache.setUsesAuthentication(this.usesAuthentication());
|
||||||
DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName());
|
DedicatedServer.LOGGER.info("Preparing level \"{}\"", this.getLevelIdName());
|
||||||
|
@ -293,7 +292,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dedicatedserverproperties.enableQuery) {
|
if (dedicatedserverproperties.enableQuery) {
|
||||||
@@ -197,7 +341,7 @@
|
@@ -197,7 +340,7 @@
|
||||||
this.rconThread = RconThread.create(this);
|
this.rconThread = RconThread.create(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +301,7 @@
|
||||||
Thread thread1 = new Thread(new ServerWatchdog(this));
|
Thread thread1 = new Thread(new ServerWatchdog(this));
|
||||||
|
|
||||||
thread1.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(DedicatedServer.LOGGER));
|
thread1.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(DedicatedServer.LOGGER));
|
||||||
@@ -215,6 +359,12 @@
|
@@ -215,6 +358,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,7 +314,7 @@
|
||||||
@Override
|
@Override
|
||||||
public boolean isSpawningMonsters() {
|
public boolean isSpawningMonsters() {
|
||||||
return this.settings.getProperties().spawnMonsters && super.isSpawningMonsters();
|
return this.settings.getProperties().spawnMonsters && super.isSpawningMonsters();
|
||||||
@@ -227,7 +377,7 @@
|
@@ -227,7 +376,7 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void forceDifficulty() {
|
public void forceDifficulty() {
|
||||||
|
@ -324,7 +323,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -286,13 +436,14 @@
|
@@ -286,13 +435,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.rconThread != null) {
|
if (this.rconThread != null) {
|
||||||
|
@ -341,7 +340,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -302,19 +453,29 @@
|
@@ -302,19 +452,29 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -377,7 +376,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -383,7 +544,7 @@
|
@@ -383,7 +543,7 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isUnderSpawnProtection(ServerLevel world, BlockPos pos, Player player) {
|
public boolean isUnderSpawnProtection(ServerLevel world, BlockPos pos, Player player) {
|
||||||
|
@ -386,7 +385,7 @@
|
||||||
return false;
|
return false;
|
||||||
} else if (this.getPlayerList().getOps().isEmpty()) {
|
} else if (this.getPlayerList().getOps().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -453,7 +614,11 @@
|
@@ -453,7 +613,11 @@
|
||||||
public boolean enforceSecureProfile() {
|
public boolean enforceSecureProfile() {
|
||||||
DedicatedServerProperties dedicatedserverproperties = this.getProperties();
|
DedicatedServerProperties dedicatedserverproperties = this.getProperties();
|
||||||
|
|
||||||
|
@ -399,7 +398,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -541,16 +706,52 @@
|
@@ -541,16 +705,52 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPluginNames() {
|
public String getPluginNames() {
|
||||||
|
@ -456,7 +455,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public void storeUsingWhiteList(boolean useWhitelist) {
|
public void storeUsingWhiteList(boolean useWhitelist) {
|
||||||
@@ -660,4 +861,15 @@
|
@@ -660,4 +860,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1558,12 +1558,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
return optional;
|
return optional;
|
||||||
@@ -1564,8 +2394,129 @@
|
@@ -1566,6 +2396,117 @@
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
+ }
|
}
|
||||||
+
|
|
||||||
+ // CraftBukkit start - add method
|
+ // CraftBukkit start - add method
|
||||||
+ public void chat(String s, PlayerChatMessage original, boolean async) {
|
+ public void chat(String s, PlayerChatMessage original, boolean async) {
|
||||||
+ if (s.isEmpty() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) {
|
+ if (s.isEmpty() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) {
|
||||||
|
@ -1656,39 +1654,29 @@
|
||||||
+ this.server.console.sendMessage(s);
|
+ this.server.console.sendMessage(s);
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
}
|
+ }
|
||||||
|
|
||||||
+ private void handleCommand(String s) {
|
|
||||||
+ org.spigotmc.AsyncCatcher.catchOp("Command Dispatched Async: " + s); // Paper - Add async catcher
|
|
||||||
+ if ( org.spigotmc.SpigotConfig.logCommands ) // Spigot
|
|
||||||
+ this.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + s);
|
|
||||||
+
|
+
|
||||||
+ CraftPlayer player = this.getCraftPlayer();
|
+ @Deprecated // Paper
|
||||||
+
|
+ public void handleCommand(String s) { // Paper - private -> public
|
||||||
+ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, s, new LazyPlayerSet(this.server));
|
+ // Paper start - Remove all this old duplicated logic
|
||||||
+ this.cserver.getPluginManager().callEvent(event);
|
+ if (s.startsWith("/")) {
|
||||||
+
|
+ s = s.substring(1);
|
||||||
+ if (event.isCancelled()) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ try {
|
|
||||||
+ if (this.cserver.dispatchCommand(event.getPlayer(), event.getMessage().substring(1))) {
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+ } catch (org.bukkit.command.CommandException ex) {
|
|
||||||
+ player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command");
|
|
||||||
+ java.util.logging.Logger.getLogger(ServerGamePacketListenerImpl.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
|
|
||||||
+ return;
|
|
||||||
+ } finally {
|
|
||||||
+ }
|
+ }
|
||||||
|
+ /*
|
||||||
|
+ It should be noted that this represents the "legacy" command execution path.
|
||||||
|
+ Api can call commands even if there is no additional context provided.
|
||||||
|
+ This method should ONLY be used if you need to execute a command WITHOUT
|
||||||
|
+ an actual player's input.
|
||||||
|
+ */
|
||||||
|
+ this.performUnsignedChatCommand(s);
|
||||||
|
+ // Paper end
|
||||||
+ }
|
+ }
|
||||||
+ // CraftBukkit end
|
+ // CraftBukkit end
|
||||||
+
|
+
|
||||||
private PlayerChatMessage getSignedMessage(ServerboundChatPacket packet, LastSeenMessages lastSeenMessages) throws SignedMessageChain.DecodeException {
|
private PlayerChatMessage getSignedMessage(ServerboundChatPacket packet, LastSeenMessages lastSeenMessages) throws SignedMessageChain.DecodeException {
|
||||||
SignedMessageBody signedmessagebody = new SignedMessageBody(packet.message(), packet.timeStamp(), packet.salt(), lastSeenMessages);
|
SignedMessageBody signedmessagebody = new SignedMessageBody(packet.message(), packet.timeStamp(), packet.salt(), lastSeenMessages);
|
||||||
|
|
||||||
@@ -1573,15 +2524,44 @@
|
@@ -1573,15 +2514,44 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
private void broadcastChatMessage(PlayerChatMessage message) {
|
private void broadcastChatMessage(PlayerChatMessage message) {
|
||||||
|
@ -1739,7 +1727,7 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1592,7 +2572,7 @@
|
@@ -1592,7 +2562,7 @@
|
||||||
synchronized (this.lastSeenMessages) {
|
synchronized (this.lastSeenMessages) {
|
||||||
if (!this.lastSeenMessages.applyOffset(packet.offset())) {
|
if (!this.lastSeenMessages.applyOffset(packet.offset())) {
|
||||||
ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString());
|
ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString());
|
||||||
|
@ -1748,7 +1736,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1601,7 +2581,40 @@
|
@@ -1601,7 +2571,40 @@
|
||||||
@Override
|
@Override
|
||||||
public void handleAnimate(ServerboundSwingPacket packet) {
|
public void handleAnimate(ServerboundSwingPacket packet) {
|
||||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
||||||
|
@ -1789,7 +1777,7 @@
|
||||||
this.player.swing(packet.getHand());
|
this.player.swing(packet.getHand());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1609,6 +2622,29 @@
|
@@ -1609,6 +2612,29 @@
|
||||||
public void handlePlayerCommand(ServerboundPlayerCommandPacket packet) {
|
public void handlePlayerCommand(ServerboundPlayerCommandPacket packet) {
|
||||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
||||||
if (this.player.hasClientLoaded()) {
|
if (this.player.hasClientLoaded()) {
|
||||||
|
@ -1819,7 +1807,7 @@
|
||||||
this.player.resetLastActionTime();
|
this.player.resetLastActionTime();
|
||||||
Entity entity;
|
Entity entity;
|
||||||
PlayerRideableJumping ijumpable;
|
PlayerRideableJumping ijumpable;
|
||||||
@@ -1616,6 +2652,11 @@
|
@@ -1616,6 +2642,11 @@
|
||||||
switch (packet.getAction()) {
|
switch (packet.getAction()) {
|
||||||
case PRESS_SHIFT_KEY:
|
case PRESS_SHIFT_KEY:
|
||||||
this.player.setShiftKeyDown(true);
|
this.player.setShiftKeyDown(true);
|
||||||
|
@ -1831,7 +1819,7 @@
|
||||||
break;
|
break;
|
||||||
case RELEASE_SHIFT_KEY:
|
case RELEASE_SHIFT_KEY:
|
||||||
this.player.setShiftKeyDown(false);
|
this.player.setShiftKeyDown(false);
|
||||||
@@ -1684,13 +2725,19 @@
|
@@ -1684,13 +2715,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i > 4096) {
|
if (i > 4096) {
|
||||||
|
@ -1852,7 +1840,7 @@
|
||||||
this.send(new ClientboundPlayerChatPacket(message.link().sender(), message.link().index(), message.signature(), message.signedBody().pack(this.messageSignatureCache), message.unsignedContent(), message.filterMask(), params));
|
this.send(new ClientboundPlayerChatPacket(message.link().sender(), message.link().index(), message.signature(), message.signedBody().pack(this.messageSignatureCache), message.unsignedContent(), message.filterMask(), params));
|
||||||
this.addPendingMessage(message);
|
this.addPendingMessage(message);
|
||||||
}
|
}
|
||||||
@@ -1701,7 +2748,19 @@
|
@@ -1701,7 +2738,19 @@
|
||||||
|
|
||||||
public SocketAddress getRemoteAddress() {
|
public SocketAddress getRemoteAddress() {
|
||||||
return this.connection.getRemoteAddress();
|
return this.connection.getRemoteAddress();
|
||||||
|
@ -1872,7 +1860,7 @@
|
||||||
|
|
||||||
public void switchToConfig() {
|
public void switchToConfig() {
|
||||||
this.waitingForSwitchToConfig = true;
|
this.waitingForSwitchToConfig = true;
|
||||||
@@ -1718,9 +2777,17 @@
|
@@ -1718,9 +2767,17 @@
|
||||||
@Override
|
@Override
|
||||||
public void handleInteract(ServerboundInteractPacket packet) {
|
public void handleInteract(ServerboundInteractPacket packet) {
|
||||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
||||||
|
@ -1890,7 +1878,7 @@
|
||||||
|
|
||||||
this.player.resetLastActionTime();
|
this.player.resetLastActionTime();
|
||||||
this.player.setShiftKeyDown(packet.isUsingSecondaryAction());
|
this.player.setShiftKeyDown(packet.isUsingSecondaryAction());
|
||||||
@@ -1733,20 +2800,59 @@
|
@@ -1733,20 +2790,59 @@
|
||||||
|
|
||||||
if (this.player.canInteractWithEntity(axisalignedbb, 3.0D)) {
|
if (this.player.canInteractWithEntity(axisalignedbb, 3.0D)) {
|
||||||
packet.dispatch(new ServerboundInteractPacket.Handler() {
|
packet.dispatch(new ServerboundInteractPacket.Handler() {
|
||||||
|
@ -1954,7 +1942,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1755,19 +2861,20 @@
|
@@ -1755,19 +2851,20 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInteraction(InteractionHand hand) {
|
public void onInteraction(InteractionHand hand) {
|
||||||
|
@ -1978,7 +1966,7 @@
|
||||||
label23:
|
label23:
|
||||||
{
|
{
|
||||||
if (entity instanceof AbstractArrow) {
|
if (entity instanceof AbstractArrow) {
|
||||||
@@ -1785,17 +2892,41 @@
|
@@ -1785,17 +2882,41 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerGamePacketListenerImpl.this.player.attack(entity);
|
ServerGamePacketListenerImpl.this.player.attack(entity);
|
||||||
|
@ -2021,7 +2009,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1809,7 +2940,7 @@
|
@@ -1809,7 +2930,7 @@
|
||||||
case PERFORM_RESPAWN:
|
case PERFORM_RESPAWN:
|
||||||
if (this.player.wonGame) {
|
if (this.player.wonGame) {
|
||||||
this.player.wonGame = false;
|
this.player.wonGame = false;
|
||||||
|
@ -2030,7 +2018,7 @@
|
||||||
this.resetPosition();
|
this.resetPosition();
|
||||||
CriteriaTriggers.CHANGED_DIMENSION.trigger(this.player, Level.END, Level.OVERWORLD);
|
CriteriaTriggers.CHANGED_DIMENSION.trigger(this.player, Level.END, Level.OVERWORLD);
|
||||||
} else {
|
} else {
|
||||||
@@ -1817,11 +2948,11 @@
|
@@ -1817,11 +2938,11 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2045,7 +2033,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1833,16 +2964,27 @@
|
@@ -1833,16 +2954,27 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleContainerClose(ServerboundContainerClosePacket packet) {
|
public void handleContainerClose(ServerboundContainerClosePacket packet) {
|
||||||
|
@ -2075,7 +2063,7 @@
|
||||||
this.player.containerMenu.sendAllDataToRemote();
|
this.player.containerMenu.sendAllDataToRemote();
|
||||||
} else if (!this.player.containerMenu.stillValid(this.player)) {
|
} else if (!this.player.containerMenu.stillValid(this.player)) {
|
||||||
ServerGamePacketListenerImpl.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu);
|
ServerGamePacketListenerImpl.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu);
|
||||||
@@ -1855,7 +2997,303 @@
|
@@ -1855,7 +2987,303 @@
|
||||||
boolean flag = packet.getStateId() != this.player.containerMenu.getStateId();
|
boolean flag = packet.getStateId() != this.player.containerMenu.getStateId();
|
||||||
|
|
||||||
this.player.containerMenu.suppressRemoteUpdates();
|
this.player.containerMenu.suppressRemoteUpdates();
|
||||||
|
@ -2380,7 +2368,7 @@
|
||||||
ObjectIterator objectiterator = Int2ObjectMaps.fastIterable(packet.getChangedSlots()).iterator();
|
ObjectIterator objectiterator = Int2ObjectMaps.fastIterable(packet.getChangedSlots()).iterator();
|
||||||
|
|
||||||
while (objectiterator.hasNext()) {
|
while (objectiterator.hasNext()) {
|
||||||
@@ -1879,6 +3317,14 @@
|
@@ -1879,6 +3307,14 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePlaceRecipe(ServerboundPlaceRecipePacket packet) {
|
public void handlePlaceRecipe(ServerboundPlaceRecipePacket packet) {
|
||||||
|
@ -2395,7 +2383,7 @@
|
||||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
||||||
this.player.resetLastActionTime();
|
this.player.resetLastActionTime();
|
||||||
if (!this.player.isSpectator() && this.player.containerMenu.containerId == packet.containerId()) {
|
if (!this.player.isSpectator() && this.player.containerMenu.containerId == packet.containerId()) {
|
||||||
@@ -1900,8 +3346,42 @@
|
@@ -1900,8 +3336,42 @@
|
||||||
ServerGamePacketListenerImpl.LOGGER.debug("Player {} tried to place impossible recipe {}", this.player, recipeholder.id().location());
|
ServerGamePacketListenerImpl.LOGGER.debug("Player {} tried to place impossible recipe {}", this.player, recipeholder.id().location());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2439,7 +2427,7 @@
|
||||||
|
|
||||||
if (containerrecipebook_a == RecipeBookMenu.PostPlaceAction.PLACE_GHOST_RECIPE) {
|
if (containerrecipebook_a == RecipeBookMenu.PostPlaceAction.PLACE_GHOST_RECIPE) {
|
||||||
this.player.connection.send(new ClientboundPlaceGhostRecipePacket(this.player.containerMenu.containerId, craftingmanager_d.display().display()));
|
this.player.connection.send(new ClientboundPlaceGhostRecipePacket(this.player.containerMenu.containerId, craftingmanager_d.display().display()));
|
||||||
@@ -1917,6 +3397,7 @@
|
@@ -1917,6 +3387,7 @@
|
||||||
@Override
|
@Override
|
||||||
public void handleContainerButtonClick(ServerboundContainerButtonClickPacket packet) {
|
public void handleContainerButtonClick(ServerboundContainerButtonClickPacket packet) {
|
||||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
||||||
|
@ -2447,7 +2435,7 @@
|
||||||
this.player.resetLastActionTime();
|
this.player.resetLastActionTime();
|
||||||
if (this.player.containerMenu.containerId == packet.containerId() && !this.player.isSpectator()) {
|
if (this.player.containerMenu.containerId == packet.containerId() && !this.player.isSpectator()) {
|
||||||
if (!this.player.containerMenu.stillValid(this.player)) {
|
if (!this.player.containerMenu.stillValid(this.player)) {
|
||||||
@@ -1945,7 +3426,44 @@
|
@@ -1945,7 +3416,44 @@
|
||||||
|
|
||||||
boolean flag1 = packet.slotNum() >= 1 && packet.slotNum() <= 45;
|
boolean flag1 = packet.slotNum() >= 1 && packet.slotNum() <= 45;
|
||||||
boolean flag2 = itemstack.isEmpty() || itemstack.getCount() <= itemstack.getMaxStackSize();
|
boolean flag2 = itemstack.isEmpty() || itemstack.getCount() <= itemstack.getMaxStackSize();
|
||||||
|
@ -2492,7 +2480,7 @@
|
||||||
if (flag1 && flag2) {
|
if (flag1 && flag2) {
|
||||||
this.player.inventoryMenu.getSlot(packet.slotNum()).setByPlayer(itemstack);
|
this.player.inventoryMenu.getSlot(packet.slotNum()).setByPlayer(itemstack);
|
||||||
this.player.inventoryMenu.setRemoteSlot(packet.slotNum(), itemstack);
|
this.player.inventoryMenu.setRemoteSlot(packet.slotNum(), itemstack);
|
||||||
@@ -1964,7 +3482,19 @@
|
@@ -1964,7 +3472,19 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleSignUpdate(ServerboundSignUpdatePacket packet) {
|
public void handleSignUpdate(ServerboundSignUpdatePacket packet) {
|
||||||
|
@ -2513,7 +2501,7 @@
|
||||||
|
|
||||||
this.filterTextPacket(list).thenAcceptAsync((list1) -> {
|
this.filterTextPacket(list).thenAcceptAsync((list1) -> {
|
||||||
this.updateSignText(packet, list1);
|
this.updateSignText(packet, list1);
|
||||||
@@ -1972,6 +3502,7 @@
|
@@ -1972,6 +3492,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSignText(ServerboundSignUpdatePacket packet, List<FilteredText> signText) {
|
private void updateSignText(ServerboundSignUpdatePacket packet, List<FilteredText> signText) {
|
||||||
|
@ -2521,7 +2509,7 @@
|
||||||
this.player.resetLastActionTime();
|
this.player.resetLastActionTime();
|
||||||
ServerLevel worldserver = this.player.serverLevel();
|
ServerLevel worldserver = this.player.serverLevel();
|
||||||
BlockPos blockposition = packet.getPos();
|
BlockPos blockposition = packet.getPos();
|
||||||
@@ -1993,15 +3524,33 @@
|
@@ -1993,15 +3514,33 @@
|
||||||
@Override
|
@Override
|
||||||
public void handlePlayerAbilities(ServerboundPlayerAbilitiesPacket packet) {
|
public void handlePlayerAbilities(ServerboundPlayerAbilitiesPacket packet) {
|
||||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
||||||
|
@ -2556,7 +2544,7 @@
|
||||||
if (this.player.isModelPartShown(PlayerModelPart.HAT) != flag) {
|
if (this.player.isModelPartShown(PlayerModelPart.HAT) != flag) {
|
||||||
this.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT, this.player));
|
this.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT, this.player));
|
||||||
}
|
}
|
||||||
@@ -2012,7 +3561,7 @@
|
@@ -2012,7 +3551,7 @@
|
||||||
public void handleChangeDifficulty(ServerboundChangeDifficultyPacket packet) {
|
public void handleChangeDifficulty(ServerboundChangeDifficultyPacket packet) {
|
||||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
|
||||||
if (this.player.hasPermissions(2) || this.isSingleplayerOwner()) {
|
if (this.player.hasPermissions(2) || this.isSingleplayerOwner()) {
|
||||||
|
@ -2565,7 +2553,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2033,7 +3582,7 @@
|
@@ -2033,7 +3572,7 @@
|
||||||
|
|
||||||
if (!Objects.equals(profilepublickey_a, profilepublickey_a1)) {
|
if (!Objects.equals(profilepublickey_a, profilepublickey_a1)) {
|
||||||
if (profilepublickey_a != null && profilepublickey_a1.expiresAt().isBefore(profilepublickey_a.expiresAt())) {
|
if (profilepublickey_a != null && profilepublickey_a1.expiresAt().isBefore(profilepublickey_a.expiresAt())) {
|
||||||
|
@ -2574,7 +2562,7 @@
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
SignatureValidator signaturevalidator = this.server.getProfileKeySignatureValidator();
|
SignatureValidator signaturevalidator = this.server.getProfileKeySignatureValidator();
|
||||||
@@ -2045,8 +3594,8 @@
|
@@ -2045,8 +3584,8 @@
|
||||||
|
|
||||||
this.resetPlayerChatState(remotechatsession_a.validate(this.player.getGameProfile(), signaturevalidator));
|
this.resetPlayerChatState(remotechatsession_a.validate(this.player.getGameProfile(), signaturevalidator));
|
||||||
} catch (ProfilePublicKey.ValidationException profilepublickey_b) {
|
} catch (ProfilePublicKey.ValidationException profilepublickey_b) {
|
||||||
|
@ -2585,7 +2573,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2058,7 +3607,7 @@
|
@@ -2058,7 +3597,7 @@
|
||||||
if (!this.waitingForSwitchToConfig) {
|
if (!this.waitingForSwitchToConfig) {
|
||||||
throw new IllegalStateException("Client acknowledged config, but none was requested");
|
throw new IllegalStateException("Client acknowledged config, but none was requested");
|
||||||
} else {
|
} else {
|
||||||
|
@ -2594,7 +2582,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2076,15 +3625,18 @@
|
@@ -2076,15 +3615,18 @@
|
||||||
|
|
||||||
private void resetPlayerChatState(RemoteChatSession session) {
|
private void resetPlayerChatState(RemoteChatSession session) {
|
||||||
this.chatSession = session;
|
this.chatSession = session;
|
||||||
|
@ -2616,7 +2604,7 @@
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleClientTickEnd(ServerboundClientTickEndPacket packet) {
|
public void handleClientTickEnd(ServerboundClientTickEndPacket packet) {
|
||||||
@@ -2115,4 +3667,17 @@
|
@@ -2115,4 +3657,17 @@
|
||||||
|
|
||||||
InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand);
|
InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
package io.papermc.paper.brigadier;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.permissions.PermissibleBase;
|
||||||
|
import org.bukkit.permissions.Permission;
|
||||||
|
import org.bukkit.permissions.PermissionAttachment;
|
||||||
|
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public final class NullCommandSender implements CommandSender {
|
||||||
|
|
||||||
|
public static final CommandSender INSTANCE = new NullCommandSender();
|
||||||
|
|
||||||
|
private NullCommandSender() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(final String message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(final String... messages) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(@Nullable final UUID sender, final String message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(@Nullable final UUID sender, final String... messages) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantValue")
|
||||||
|
@Override
|
||||||
|
public Server getServer() {
|
||||||
|
final @Nullable Server server = Bukkit.getServer();
|
||||||
|
if (server == null) {
|
||||||
|
throw new UnsupportedOperationException("The server has not been created yet, you cannot access it at this time from the 'null' CommandSender");
|
||||||
|
}
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Spigot spigot = new Spigot();
|
||||||
|
@Override
|
||||||
|
public Spigot spigot() {
|
||||||
|
return this.spigot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Spigot extends CommandSender.Spigot {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(@NotNull final BaseComponent component) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(@NonNull final @NotNull BaseComponent... components) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(@Nullable final UUID sender, @NotNull final BaseComponent component) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(@Nullable final UUID sender, @NonNull final @NotNull BaseComponent... components) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component name() {
|
||||||
|
return Component.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPermissionSet(final String name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPermissionSet(final Permission perm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(final String name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPermission(final Permission perm) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PermissionAttachment addAttachment(final Plugin plugin, final String name, final boolean value) {
|
||||||
|
throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PermissionAttachment addAttachment(final Plugin plugin) {
|
||||||
|
throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable PermissionAttachment addAttachment(final Plugin plugin, final String name, final boolean value, final int ticks) {
|
||||||
|
throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable PermissionAttachment addAttachment(final Plugin plugin, final int ticks) {
|
||||||
|
throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAttachment(final PermissionAttachment attachment) {
|
||||||
|
throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recalculatePermissions() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
|
||||||
|
throw new UnsupportedOperationException("Cannot remove attachments from the 'null' CommandSender");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOp() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOp(final boolean value) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
package io.papermc.paper.brigadier;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.Message;
|
|
||||||
import io.papermc.paper.adventure.PaperAdventure;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.ComponentLike;
|
|
||||||
import net.minecraft.network.chat.ComponentUtils;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
public enum PaperBrigadierProviderImpl implements PaperBrigadierProvider {
|
|
||||||
INSTANCE;
|
|
||||||
|
|
||||||
PaperBrigadierProviderImpl() {
|
|
||||||
PaperBrigadierProvider.initialize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Message message(final @NonNull ComponentLike componentLike) {
|
|
||||||
requireNonNull(componentLike, "componentLike");
|
|
||||||
return PaperAdventure.asVanilla(componentLike.asComponent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull Component componentFromMessage(final @NonNull Message message) {
|
|
||||||
requireNonNull(message, "message");
|
|
||||||
return PaperAdventure.asAdventure(ComponentUtils.fromMessage(message));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.FloatArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.LongArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
||||||
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
|
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import com.mojang.brigadier.tree.RootCommandNode;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.WrappedArgumentCommandNode;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This root command node is responsible for wrapping around vanilla's dispatcher.
|
||||||
|
* <p>
|
||||||
|
* The reason for this is conversion is we do NOT want there to be NMS types
|
||||||
|
* in the api. This allows us to reconstruct the nodes to be more api friendly, while
|
||||||
|
* we can then unwrap it when needed and convert them to NMS types.
|
||||||
|
* <p>
|
||||||
|
* Command nodes such as vanilla (those without a proper "api node")
|
||||||
|
* will be assigned a {@link ShadowBrigNode}.
|
||||||
|
* This prevents certain parts of it (children) from being accessed by the api.
|
||||||
|
*/
|
||||||
|
public abstract class ApiMirrorRootNode extends RootCommandNode<CommandSourceStack> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents argument types that are allowed to exist in the api.
|
||||||
|
* These typically represent primitives that don't need to be wrapped
|
||||||
|
* by NMS.
|
||||||
|
*/
|
||||||
|
private static final Set<Class<? extends ArgumentType<?>>> ARGUMENT_WHITELIST = Set.of(
|
||||||
|
BoolArgumentType.class,
|
||||||
|
DoubleArgumentType.class,
|
||||||
|
FloatArgumentType.class,
|
||||||
|
IntegerArgumentType.class,
|
||||||
|
LongArgumentType.class,
|
||||||
|
StringArgumentType.class
|
||||||
|
);
|
||||||
|
|
||||||
|
public static void validatePrimitiveType(ArgumentType<?> type) {
|
||||||
|
if (ARGUMENT_WHITELIST.contains(type.getClass())) {
|
||||||
|
if (!ArgumentTypeInfos.isClassRecognized(type.getClass())) {
|
||||||
|
throw new IllegalArgumentException("This whitelisted primitive argument type is not recognized by the server!");
|
||||||
|
}
|
||||||
|
} else if (!(type instanceof VanillaArgumentProviderImpl.NativeWrapperArgumentType<?,?> nativeWrapperArgumentType) || !ArgumentTypeInfos.isClassRecognized(nativeWrapperArgumentType.nativeNmsArgumentType().getClass())) {
|
||||||
|
throw new IllegalArgumentException("Custom argument type was passed, this was not a recognized type to send to the client! You must only pass vanilla arguments or primitive brig args in the wrapper!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract CommandDispatcher<net.minecraft.commands.CommandSourceStack> getDispatcher();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This logic is responsible for unwrapping an API node to be supported by NMS.
|
||||||
|
* See the method implementation for detailed steps.
|
||||||
|
*
|
||||||
|
* @param maybeWrappedNode api provided node / node to be "wrapped"
|
||||||
|
* @return wrapped node
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
private @NotNull CommandNode<CommandSourceStack> unwrapNode(final CommandNode<CommandSourceStack> maybeWrappedNode) {
|
||||||
|
/*
|
||||||
|
If the type is a shadow node we can assume that the type that it represents is an already supported NMS node.
|
||||||
|
This is because these are typically minecraft command nodes.
|
||||||
|
*/
|
||||||
|
if (maybeWrappedNode instanceof final ShadowBrigNode shadowBrigNode) {
|
||||||
|
return (CommandNode) shadowBrigNode.getHandle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This node already has had an unwrapped node created, so we can assume that it's safe to reuse that cached copy.
|
||||||
|
*/
|
||||||
|
if (maybeWrappedNode.unwrappedCached != null) {
|
||||||
|
return maybeWrappedNode.unwrappedCached;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the pure brig node into one compatible with the nms dispatcher
|
||||||
|
return this.convertFromPureBrigNode(maybeWrappedNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull CommandNode<CommandSourceStack> convertFromPureBrigNode(final CommandNode<CommandSourceStack> pureNode) {
|
||||||
|
/*
|
||||||
|
Logic for converting a node.
|
||||||
|
*/
|
||||||
|
final CommandNode<CommandSourceStack> converted;
|
||||||
|
if (pureNode instanceof final LiteralCommandNode<CommandSourceStack> node) {
|
||||||
|
/*
|
||||||
|
Remap the literal node, we only have to account
|
||||||
|
for the redirect in this case.
|
||||||
|
*/
|
||||||
|
converted = this.simpleUnwrap(node);
|
||||||
|
} else if (pureNode instanceof final ArgumentCommandNode<CommandSourceStack, ?> pureArgumentNode) {
|
||||||
|
final ArgumentType<?> pureArgumentType = pureArgumentNode.getType();
|
||||||
|
/*
|
||||||
|
Check to see if this argument type is a wrapped type, if so we know that
|
||||||
|
we can unwrap the node to get an NMS type.
|
||||||
|
*/
|
||||||
|
if (pureArgumentType instanceof final CustomArgumentType<?, ?> customArgumentType) {
|
||||||
|
final SuggestionProvider<?> suggestionProvider;
|
||||||
|
try {
|
||||||
|
final Method listSuggestions = customArgumentType.getClass().getMethod("listSuggestions", CommandContext.class, SuggestionsBuilder.class);
|
||||||
|
if (listSuggestions.getDeclaringClass() != CustomArgumentType.class) {
|
||||||
|
suggestionProvider = customArgumentType::listSuggestions;
|
||||||
|
} else {
|
||||||
|
suggestionProvider = null;
|
||||||
|
}
|
||||||
|
} catch (final NoSuchMethodException ex) {
|
||||||
|
throw new IllegalStateException("Could not determine if the custom argument type " + customArgumentType + " overrides listSuggestions", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
converted = this.unwrapArgumentWrapper(pureArgumentNode, customArgumentType, customArgumentType.getNativeType(), suggestionProvider);
|
||||||
|
} else if (pureArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?,?> nativeWrapperArgumentType) {
|
||||||
|
converted = this.unwrapArgumentWrapper(pureArgumentNode, nativeWrapperArgumentType, nativeWrapperArgumentType, null); // "null" for suggestion provider so it uses the argument type's suggestion provider
|
||||||
|
|
||||||
|
/*
|
||||||
|
If it's not a wrapped type, it either has to be a primitive or an already
|
||||||
|
defined NMS type.
|
||||||
|
This method allows us to check if this is recognized by vanilla.
|
||||||
|
*/
|
||||||
|
} else if (ArgumentTypeInfos.isClassRecognized(pureArgumentType.getClass())) {
|
||||||
|
// Allow any type of argument, as long as it's recognized by the client (but in most cases, this should be API only types)
|
||||||
|
// Previously we only allowed whitelisted types.
|
||||||
|
converted = this.simpleUnwrap(pureArgumentNode);
|
||||||
|
} else {
|
||||||
|
// Unknown argument type was passed
|
||||||
|
throw new IllegalArgumentException("Custom unknown argument type was passed, should be wrapped inside an CustomArgumentType.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown command node passed. Don't know how to unwrap this.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store unwrapped node before unwrapping children to avoid infinite recursion in cyclic redirects.
|
||||||
|
converted.wrappedCached = pureNode;
|
||||||
|
pureNode.unwrappedCached = converted;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Add the children to the node, unwrapping each child in the process.
|
||||||
|
*/
|
||||||
|
for (final CommandNode<CommandSourceStack> child : pureNode.getChildren()) {
|
||||||
|
converted.addChild(this.unwrapNode(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This logic is responsible for rewrapping a node.
|
||||||
|
* If a node was unwrapped in the past, it should have a wrapped type
|
||||||
|
* stored in its cache.
|
||||||
|
* <p>
|
||||||
|
* However, if it doesn't seem to have a wrapped version we will return
|
||||||
|
* a {@link ShadowBrigNode} instead. This supports being unwrapped/wrapped while
|
||||||
|
* preventing the API from accessing it unsafely.
|
||||||
|
*
|
||||||
|
* @param unwrapped argument node
|
||||||
|
* @return wrapped node
|
||||||
|
*/
|
||||||
|
private @Nullable CommandNode<CommandSourceStack> wrapNode(@Nullable final CommandNode<net.minecraft.commands.CommandSourceStack> unwrapped) {
|
||||||
|
if (unwrapped == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This was most likely created by API and has a wrapped variant,
|
||||||
|
so we can return this safely.
|
||||||
|
*/
|
||||||
|
if (unwrapped.wrappedCached != null) {
|
||||||
|
return unwrapped.wrappedCached;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
We don't know the type of this, or where this came from.
|
||||||
|
Return a shadow, where we will allow the api to handle this but have
|
||||||
|
restrictive access.
|
||||||
|
*/
|
||||||
|
CommandNode<CommandSourceStack> shadow = new ShadowBrigNode(unwrapped);
|
||||||
|
unwrapped.wrappedCached = shadow;
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nodes added to this dispatcher must be unwrapped
|
||||||
|
* in order to be added to the NMS dispatcher.
|
||||||
|
*
|
||||||
|
* @param node node to add
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
@Override
|
||||||
|
public void addChild(CommandNode<CommandSourceStack> node) {
|
||||||
|
CommandNode convertedNode = this.unwrapNode(node);
|
||||||
|
this.getDispatcher().getRoot().addChild(convertedNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the children for the vanilla dispatcher,
|
||||||
|
* ensuring that all are wrapped.
|
||||||
|
*
|
||||||
|
* @return wrapped children
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<CommandNode<CommandSourceStack>> getChildren() {
|
||||||
|
return Collections2.transform(this.getDispatcher().getRoot().getChildren(), this::wrapNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandNode<CommandSourceStack> getChild(String name) {
|
||||||
|
return this.wrapNode(this.getDispatcher().getRoot().getChild(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are needed for bukkit... we should NOT allow this
|
||||||
|
@Override
|
||||||
|
public void removeCommand(String name) {
|
||||||
|
this.getDispatcher().getRoot().removeCommand(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearAll() {
|
||||||
|
this.getDispatcher().getRoot().clearAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
private CommandNode<CommandSourceStack> unwrapArgumentWrapper(final ArgumentCommandNode pureNode, final ArgumentType pureArgumentType, final ArgumentType possiblyWrappedNativeArgumentType, @Nullable SuggestionProvider argumentTypeSuggestionProvider) {
|
||||||
|
validatePrimitiveType(possiblyWrappedNativeArgumentType);
|
||||||
|
final CommandNode redirectNode = pureNode.getRedirect() == null ? null : this.unwrapNode(pureNode.getRedirect());
|
||||||
|
// If there is already a custom suggestion provider, ignore the suggestion provider from the argument type
|
||||||
|
final SuggestionProvider suggestionProvider = pureNode.getCustomSuggestions() != null ? pureNode.getCustomSuggestions() : argumentTypeSuggestionProvider;
|
||||||
|
|
||||||
|
final ArgumentType nativeArgumentType = possiblyWrappedNativeArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?,?> nativeWrapperArgumentType ? nativeWrapperArgumentType.nativeNmsArgumentType() : possiblyWrappedNativeArgumentType;
|
||||||
|
return new WrappedArgumentCommandNode<>(pureNode.getName(), pureArgumentType, nativeArgumentType, pureNode.getCommand(), pureNode.getRequirement(), redirectNode, pureNode.getRedirectModifier(), pureNode.isFork(), suggestionProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandNode<CommandSourceStack> simpleUnwrap(final CommandNode<CommandSourceStack> node) {
|
||||||
|
return node.createBuilder()
|
||||||
|
.forward(node.getRedirect() == null ? null : this.unwrapNode(node.getRedirect()), node.getRedirectModifier(), node.isFork())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.Message;
|
||||||
|
import io.papermc.paper.adventure.PaperAdventure;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.minecraft.network.chat.ComponentUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public final class MessageComponentSerializerImpl implements MessageComponentSerializer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Component deserialize(@NotNull Message input) {
|
||||||
|
return PaperAdventure.asAdventure(ComponentUtils.fromMessage(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Message serialize(@NotNull Component component) {
|
||||||
|
return PaperAdventure.asVanilla(component);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap;
|
||||||
|
import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode;
|
||||||
|
import net.minecraft.commands.CommandSource;
|
||||||
|
import net.minecraft.commands.Commands;
|
||||||
|
import net.minecraft.network.chat.CommonComponents;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.world.phys.Vec2;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandMap;
|
||||||
|
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class PaperBrigadier {
|
||||||
|
|
||||||
|
@SuppressWarnings("DataFlowIssue")
|
||||||
|
static final net.minecraft.commands.CommandSourceStack DUMMY = new net.minecraft.commands.CommandSourceStack(
|
||||||
|
CommandSource.NULL,
|
||||||
|
Vec3.ZERO,
|
||||||
|
Vec2.ZERO,
|
||||||
|
null,
|
||||||
|
4,
|
||||||
|
"",
|
||||||
|
CommonComponents.EMPTY,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
public static Command wrapNode(CommandNode node) {
|
||||||
|
if (!(node instanceof LiteralCommandNode)) {
|
||||||
|
throw new IllegalArgumentException("Unsure how to wrap a " + node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(node instanceof PluginCommandNode pluginCommandNode)) {
|
||||||
|
return new VanillaCommandWrapper(null, node);
|
||||||
|
}
|
||||||
|
CommandNode<CommandSourceStack> argumentCommandNode = node;
|
||||||
|
if (argumentCommandNode.getRedirect() != null) {
|
||||||
|
argumentCommandNode = argumentCommandNode.getRedirect();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<CommandNode<CommandSourceStack>, String> map = PaperCommands.INSTANCE.getDispatcherInternal().getSmartUsage(argumentCommandNode, DUMMY);
|
||||||
|
String usage = map.isEmpty() ? pluginCommandNode.getUsageText() : pluginCommandNode.getUsageText() + " " + String.join("\n" + pluginCommandNode.getUsageText() + " ", map.values());
|
||||||
|
return new PluginVanillaCommandWrapper(pluginCommandNode.getName(), pluginCommandNode.getDescription(), usage, pluginCommandNode.getAliases(), node, pluginCommandNode.getPlugin());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Previously, Bukkit used one command dispatcher and ignored minecraft's reloading logic.
|
||||||
|
|
||||||
|
In order to allow for legacy commands to be properly added, we will iterate through previous bukkit commands
|
||||||
|
in the old dispatcher and re-register them.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
public static void moveBukkitCommands(Commands before, Commands after) {
|
||||||
|
CommandDispatcher erasedDispatcher = before.getDispatcher();
|
||||||
|
|
||||||
|
for (Object node : erasedDispatcher.getRoot().getChildren()) {
|
||||||
|
if (node instanceof CommandNode<?> commandNode && commandNode.getCommand() instanceof BukkitCommandNode.BukkitBrigCommand) {
|
||||||
|
after.getDispatcher().getRoot().removeCommand(((CommandNode<?>) node).getName()); // Remove already existing commands
|
||||||
|
after.getDispatcher().getRoot().addChild((CommandNode<net.minecraft.commands.CommandSourceStack>) node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec2;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface PaperCommandSourceStack extends CommandSourceStack, BukkitBrigadierCommandSource {
|
||||||
|
|
||||||
|
net.minecraft.commands.CommandSourceStack getHandle();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default @NotNull Location getLocation() {
|
||||||
|
Vec2 rot = this.getHandle().getRotation();
|
||||||
|
Vec3 pos = this.getHandle().getPosition();
|
||||||
|
Level level = this.getHandle().getLevel();
|
||||||
|
|
||||||
|
return new Location(level.getWorld(), pos.x, pos.y, pos.z, rot.y, rot.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
default CommandSender getSender() {
|
||||||
|
return this.getHandle().getBukkitSender();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
default Entity getExecutor() {
|
||||||
|
net.minecraft.world.entity.Entity nmsEntity = this.getHandle().getEntity();
|
||||||
|
if (nmsEntity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nmsEntity.getBukkitEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
// OLD METHODS
|
||||||
|
@Override
|
||||||
|
default org.bukkit.entity.Entity getBukkitEntity() {
|
||||||
|
return this.getExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default org.bukkit.World getBukkitWorld() {
|
||||||
|
return this.getLocation().getWorld();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default org.bukkit.Location getBukkitLocation() {
|
||||||
|
return this.getLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default CommandSender getBukkitSender() {
|
||||||
|
return this.getSender();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,204 @@
|
||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode;
|
||||||
|
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||||
|
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
|
||||||
|
import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import net.minecraft.commands.CommandBuildContext;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public class PaperCommands implements Commands, PaperRegistrar<LifecycleEventOwner> {
|
||||||
|
|
||||||
|
public static final PaperCommands INSTANCE = new PaperCommands();
|
||||||
|
|
||||||
|
private @Nullable LifecycleEventOwner currentContext;
|
||||||
|
private @MonotonicNonNull CommandDispatcher<CommandSourceStack> dispatcher;
|
||||||
|
private @MonotonicNonNull CommandBuildContext buildContext;
|
||||||
|
private boolean invalid = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCurrentContext(final @Nullable LifecycleEventOwner context) {
|
||||||
|
this.currentContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDispatcher(final net.minecraft.commands.Commands commands, final CommandBuildContext commandBuildContext) {
|
||||||
|
this.invalid = false;
|
||||||
|
this.dispatcher = new CommandDispatcher<>(new ApiMirrorRootNode() {
|
||||||
|
@Override
|
||||||
|
public CommandDispatcher<net.minecraft.commands.CommandSourceStack> getDispatcher() {
|
||||||
|
return commands.getDispatcher();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.buildContext = commandBuildContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValid() {
|
||||||
|
this.invalid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
this.invalid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use this method internally as it bypasses the valid check
|
||||||
|
public CommandDispatcher<CommandSourceStack> getDispatcherInternal() {
|
||||||
|
Preconditions.checkState(this.dispatcher != null, "the dispatcher hasn't been set yet");
|
||||||
|
return this.dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandBuildContext getBuildContext() {
|
||||||
|
Preconditions.checkState(this.buildContext != null, "the build context hasn't been set yet");
|
||||||
|
return this.buildContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandDispatcher<CommandSourceStack> getDispatcher() {
|
||||||
|
Preconditions.checkState(!this.invalid && this.dispatcher != null, "cannot access the dispatcher in this context");
|
||||||
|
return this.dispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Unmodifiable Set<String> register(final LiteralCommandNode<CommandSourceStack> node, final @Nullable String description, final Collection<String> aliases) {
|
||||||
|
return this.register(requireNonNull(this.currentContext, "No lifecycle owner context is set").getPluginMeta(), node, description, aliases);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Unmodifiable Set<String> register(final PluginMeta pluginMeta, final LiteralCommandNode<CommandSourceStack> node, final @Nullable String description, final Collection<String> aliases) {
|
||||||
|
return this.registerWithFlags(pluginMeta, node, description, aliases, Set.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Unmodifiable Set<String> registerWithFlags(@NotNull final PluginMeta pluginMeta, @NotNull final LiteralCommandNode<CommandSourceStack> node, @org.jetbrains.annotations.Nullable final String description, @NotNull final Collection<String> aliases, @NotNull final Set<CommandRegistrationFlag> flags) {
|
||||||
|
final boolean hasFlattenRedirectFlag = flags.contains(CommandRegistrationFlag.FLATTEN_ALIASES);
|
||||||
|
final String identifier = pluginMeta.getName().toLowerCase(Locale.ROOT);
|
||||||
|
final String literal = node.getLiteral();
|
||||||
|
final PluginCommandNode pluginLiteral = new PluginCommandNode(identifier + ":" + literal, pluginMeta, node, description); // Treat the keyed version of the command as the root
|
||||||
|
|
||||||
|
final Set<String> registeredLabels = new HashSet<>(aliases.size() * 2 + 2);
|
||||||
|
|
||||||
|
if (this.registerIntoDispatcher(pluginLiteral, true)) {
|
||||||
|
registeredLabels.add(pluginLiteral.getLiteral());
|
||||||
|
}
|
||||||
|
if (this.registerRedirect(literal, pluginMeta, pluginLiteral, description, true, hasFlattenRedirectFlag)) { // Plugin commands should override vanilla commands
|
||||||
|
registeredLabels.add(literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add aliases
|
||||||
|
final List<String> registeredAliases = new ArrayList<>(aliases.size() * 2);
|
||||||
|
for (final String alias : aliases) {
|
||||||
|
if (this.registerRedirect(alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
|
||||||
|
registeredAliases.add(alias);
|
||||||
|
}
|
||||||
|
if (this.registerRedirect(identifier + ":" + alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
|
||||||
|
registeredAliases.add(identifier + ":" + alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!registeredAliases.isEmpty()) {
|
||||||
|
pluginLiteral.setAliases(registeredAliases);
|
||||||
|
}
|
||||||
|
|
||||||
|
registeredLabels.addAll(registeredAliases);
|
||||||
|
return registeredLabels.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(registeredLabels);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean registerRedirect(final String aliasLiteral, final PluginMeta plugin, final PluginCommandNode redirectTo, final @Nullable String description, final boolean override, boolean hasFlattenRedirectFlag) {
|
||||||
|
final LiteralCommandNode<CommandSourceStack> redirect;
|
||||||
|
if (redirectTo.getChildren().isEmpty() || hasFlattenRedirectFlag) {
|
||||||
|
redirect = Commands.literal(aliasLiteral)
|
||||||
|
.executes(redirectTo.getCommand())
|
||||||
|
.requires(redirectTo.getRequirement())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
for (final CommandNode<CommandSourceStack> child : redirectTo.getChildren()) {
|
||||||
|
redirect.addChild(child);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
redirect = Commands.literal(aliasLiteral)
|
||||||
|
.executes(redirectTo.getCommand())
|
||||||
|
.redirect(redirectTo)
|
||||||
|
.requires(redirectTo.getRequirement())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.registerIntoDispatcher(new PluginCommandNode(aliasLiteral, plugin, redirect, description), override);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean registerIntoDispatcher(final PluginCommandNode node, boolean override) {
|
||||||
|
final @Nullable CommandNode<CommandSourceStack> existingChild = this.getDispatcher().getRoot().getChild(node.getLiteral());
|
||||||
|
if (existingChild != null && !(existingChild instanceof PluginCommandNode) && !(existingChild instanceof BukkitCommandNode)) {
|
||||||
|
override = true; // override vanilla commands
|
||||||
|
}
|
||||||
|
if (existingChild == null || override) { // Avoid merging behavior. Maybe something to look into in the future
|
||||||
|
if (override) {
|
||||||
|
this.getDispatcher().getRoot().removeCommand(node.getLiteral());
|
||||||
|
}
|
||||||
|
this.getDispatcher().getRoot().addChild(node);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Unmodifiable Set<String> register(final String label, final @Nullable String description, final Collection<String> aliases, final BasicCommand basicCommand) {
|
||||||
|
return this.register(requireNonNull(this.currentContext, "No lifecycle owner context is set").getPluginMeta(), label, description, aliases, basicCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Unmodifiable Set<String> register(final PluginMeta pluginMeta, final String label, final @Nullable String description, final Collection<String> aliases, final BasicCommand basicCommand) {
|
||||||
|
final LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(label)
|
||||||
|
.requires(stack -> basicCommand.canUse(stack.getSender()))
|
||||||
|
.then(
|
||||||
|
Commands.argument("args", StringArgumentType.greedyString())
|
||||||
|
.suggests((context, suggestionsBuilder) -> {
|
||||||
|
String[] args = StringUtils.split(suggestionsBuilder.getRemaining());
|
||||||
|
if (suggestionsBuilder.getRemaining().endsWith(" ")) {
|
||||||
|
// if there is trailing whitespace, we should add an empty argument to signify
|
||||||
|
// that there may be more, but no characters have been typed yet
|
||||||
|
args = ArrayUtils.add(args, "");
|
||||||
|
}
|
||||||
|
final SuggestionsBuilder offsetSuggestionsBuilder = suggestionsBuilder.createOffset(suggestionsBuilder.getInput().lastIndexOf(' ') + 1);
|
||||||
|
|
||||||
|
final Collection<String> suggestions = basicCommand.suggest(context.getSource(), args);
|
||||||
|
suggestions.forEach(offsetSuggestionsBuilder::suggest);
|
||||||
|
return offsetSuggestionsBuilder.buildFuture();
|
||||||
|
})
|
||||||
|
.executes((stack) -> {
|
||||||
|
basicCommand.execute(stack.getSource(), StringUtils.split(stack.getArgument("args", String.class), ' '));
|
||||||
|
return com.mojang.brigadier.Command.SINGLE_SUCCESS;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.executes((stack) -> {
|
||||||
|
basicCommand.execute(stack.getSource(), new String[0]);
|
||||||
|
return com.mojang.brigadier.Command.SINGLE_SUCCESS;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.register(pluginMeta, builder.build(), description, aliases);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class PluginCommandNode extends LiteralCommandNode<CommandSourceStack> {
|
||||||
|
|
||||||
|
private final PluginMeta plugin;
|
||||||
|
private final String description;
|
||||||
|
private List<String> aliases = Collections.emptyList();
|
||||||
|
|
||||||
|
public PluginCommandNode(final @NotNull String literal, final @NotNull PluginMeta plugin, final @NotNull LiteralCommandNode<CommandSourceStack> rootLiteral, final @Nullable String description) {
|
||||||
|
super(
|
||||||
|
literal, rootLiteral.getCommand(), rootLiteral.getRequirement(),
|
||||||
|
rootLiteral.getRedirect(), rootLiteral.getRedirectModifier(), rootLiteral.isFork()
|
||||||
|
);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.description = description;
|
||||||
|
|
||||||
|
for (CommandNode<CommandSourceStack> argument : rootLiteral.getChildren()) {
|
||||||
|
this.addChild(argument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Plugin getPlugin() {
|
||||||
|
return Objects.requireNonNull(Bukkit.getPluginManager().getPlugin(this.plugin.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAliases(List<String> aliases) {
|
||||||
|
this.aliases = aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getAliases() {
|
||||||
|
return this.aliases;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.commands.Commands;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.PluginIdentifiableCommand;
|
||||||
|
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// Exists to that /help can show the plugin
|
||||||
|
public class PluginVanillaCommandWrapper extends VanillaCommandWrapper implements PluginIdentifiableCommand {
|
||||||
|
|
||||||
|
private final Plugin plugin;
|
||||||
|
private final List<String> alises;
|
||||||
|
|
||||||
|
public PluginVanillaCommandWrapper(String name, String description, String usageMessage, List<String> aliases, CommandNode<CommandSourceStack> vanillaCommand, Plugin plugin) {
|
||||||
|
super(name, description, usageMessage, aliases, vanillaCommand);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.alises = aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<String> getAliases() {
|
||||||
|
return this.alises;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Command setAliases(@NotNull List<String> aliases) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Plugin getPlugin() {
|
||||||
|
return this.plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show in help menu!
|
||||||
|
@Override
|
||||||
|
public boolean isRegistered() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class ShadowBrigNode extends LiteralCommandNode<CommandSourceStack> {
|
||||||
|
|
||||||
|
private final CommandNode<net.minecraft.commands.CommandSourceStack> handle;
|
||||||
|
|
||||||
|
public ShadowBrigNode(CommandNode<net.minecraft.commands.CommandSourceStack> node) {
|
||||||
|
super(node.getName(), context -> 0, (s) -> false, node.getRedirect() == null ? null : new ShadowBrigNode(node.getRedirect()), null, node.isFork());
|
||||||
|
this.handle = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<CommandNode<CommandSourceStack>> getChildren() {
|
||||||
|
throw new UnsupportedOperationException("Cannot retrieve children from this node.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandNode<CommandSourceStack> getChild(String name) {
|
||||||
|
throw new UnsupportedOperationException("Cannot retrieve children from this node.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addChild(CommandNode<CommandSourceStack> node) {
|
||||||
|
throw new UnsupportedOperationException("Cannot modify children for this node.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandNode<net.minecraft.commands.CommandSourceStack> getHandle() {
|
||||||
|
return this.handle;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package io.papermc.paper.command.brigadier.argument;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import net.kyori.adventure.chat.SignedMessage;
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.commands.arguments.MessageArgument;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
public record SignedMessageResolverImpl(MessageArgument.Message message) implements SignedMessageResolver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String content() {
|
||||||
|
return this.message.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<SignedMessage> resolveSignedMessage(final String argumentName, final CommandContext erased) throws CommandSyntaxException {
|
||||||
|
final CompletableFuture<SignedMessage> future = new CompletableFuture<>();
|
||||||
|
|
||||||
|
final MessageArgument.Message response = ((CommandContext<CommandSourceStack>) erased).getArgument(argumentName, SignedMessageResolverImpl.class).message;
|
||||||
|
MessageArgument.resolveChatMessage(response, erased, argumentName, (message) -> {
|
||||||
|
future.complete(message.adventureView());
|
||||||
|
});
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,366 @@
|
||||||
|
package io.papermc.paper.command.brigadier.argument;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.profile.CraftPlayerProfile;
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import com.mojang.brigadier.StringReader;
|
||||||
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
|
import io.papermc.paper.adventure.PaperAdventure;
|
||||||
|
import io.papermc.paper.command.brigadier.PaperCommands;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.predicate.ItemStackPredicate;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.range.DoubleRangeProvider;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.range.IntegerRangeProvider;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.range.RangeProvider;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.resolvers.FinePositionResolver;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
|
||||||
|
import io.papermc.paper.entity.LookAnchor;
|
||||||
|
import io.papermc.paper.registry.PaperRegistries;
|
||||||
|
import io.papermc.paper.registry.RegistryAccess;
|
||||||
|
import io.papermc.paper.registry.RegistryKey;
|
||||||
|
import io.papermc.paper.registry.TypedKey;
|
||||||
|
import io.papermc.paper.util.MCUtil;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.kyori.adventure.text.format.Style;
|
||||||
|
import net.minecraft.advancements.critereon.MinMaxBounds;
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.commands.arguments.ColorArgument;
|
||||||
|
import net.minecraft.commands.arguments.ComponentArgument;
|
||||||
|
import net.minecraft.commands.arguments.DimensionArgument;
|
||||||
|
import net.minecraft.commands.arguments.EntityAnchorArgument;
|
||||||
|
import net.minecraft.commands.arguments.EntityArgument;
|
||||||
|
import net.minecraft.commands.arguments.GameModeArgument;
|
||||||
|
import net.minecraft.commands.arguments.GameProfileArgument;
|
||||||
|
import net.minecraft.commands.arguments.HeightmapTypeArgument;
|
||||||
|
import net.minecraft.commands.arguments.MessageArgument;
|
||||||
|
import net.minecraft.commands.arguments.ObjectiveCriteriaArgument;
|
||||||
|
import net.minecraft.commands.arguments.RangeArgument;
|
||||||
|
import net.minecraft.commands.arguments.ResourceArgument;
|
||||||
|
import net.minecraft.commands.arguments.ResourceKeyArgument;
|
||||||
|
import net.minecraft.commands.arguments.ResourceLocationArgument;
|
||||||
|
import net.minecraft.commands.arguments.ScoreboardSlotArgument;
|
||||||
|
import net.minecraft.commands.arguments.StyleArgument;
|
||||||
|
import net.minecraft.commands.arguments.TemplateMirrorArgument;
|
||||||
|
import net.minecraft.commands.arguments.TemplateRotationArgument;
|
||||||
|
import net.minecraft.commands.arguments.TimeArgument;
|
||||||
|
import net.minecraft.commands.arguments.UuidArgument;
|
||||||
|
import net.minecraft.commands.arguments.blocks.BlockStateArgument;
|
||||||
|
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
|
||||||
|
import net.minecraft.commands.arguments.coordinates.Vec3Argument;
|
||||||
|
import net.minecraft.commands.arguments.item.ItemArgument;
|
||||||
|
import net.minecraft.commands.arguments.item.ItemPredicateArgument;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
import org.bukkit.HeightMap;
|
||||||
|
import org.bukkit.Keyed;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.BlockState;
|
||||||
|
import org.bukkit.block.structure.Mirror;
|
||||||
|
import org.bukkit.block.structure.StructureRotation;
|
||||||
|
import org.bukkit.craftbukkit.CraftHeightMap;
|
||||||
|
import org.bukkit.craftbukkit.CraftRegistry;
|
||||||
|
import org.bukkit.craftbukkit.block.CraftBlockStates;
|
||||||
|
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||||
|
import org.bukkit.craftbukkit.scoreboard.CraftCriteria;
|
||||||
|
import org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations;
|
||||||
|
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.scoreboard.Criteria;
|
||||||
|
import org.bukkit.scoreboard.DisplaySlot;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@DefaultQualifier(NonNull.class)
|
||||||
|
public class VanillaArgumentProviderImpl implements VanillaArgumentProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<EntitySelectorArgumentResolver> entity() {
|
||||||
|
return this.wrap(EntityArgument.entity(), (result) -> sourceStack -> {
|
||||||
|
return List.of(result.findSingleEntity((CommandSourceStack) sourceStack).getBukkitEntity());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<EntitySelectorArgumentResolver> entities() {
|
||||||
|
return this.wrap(EntityArgument.entities(), (result) -> sourceStack -> {
|
||||||
|
return Lists.transform(result.findEntities((CommandSourceStack) sourceStack), net.minecraft.world.entity.Entity::getBukkitEntity);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<PlayerSelectorArgumentResolver> player() {
|
||||||
|
return this.wrap(EntityArgument.player(), (result) -> sourceStack -> {
|
||||||
|
return List.of(result.findSinglePlayer((CommandSourceStack) sourceStack).getBukkitEntity());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<PlayerSelectorArgumentResolver> players() {
|
||||||
|
return this.wrap(EntityArgument.players(), (result) -> sourceStack -> {
|
||||||
|
return Lists.transform(result.findPlayers((CommandSourceStack) sourceStack), ServerPlayer::getBukkitEntity);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<PlayerProfileListResolver> playerProfiles() {
|
||||||
|
return this.wrap(GameProfileArgument.gameProfile(), result -> {
|
||||||
|
if (result instanceof GameProfileArgument.SelectorResult) {
|
||||||
|
return sourceStack -> Collections.unmodifiableCollection(Collections2.transform(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new));
|
||||||
|
} else {
|
||||||
|
return sourceStack -> Collections.unmodifiableCollection(Collections2.transform(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<BlockPositionResolver> blockPosition() {
|
||||||
|
return this.wrap(BlockPosArgument.blockPos(), (result) -> sourceStack -> {
|
||||||
|
final BlockPos pos = result.getBlockPos((CommandSourceStack) sourceStack);
|
||||||
|
|
||||||
|
return MCUtil.toPosition(pos);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<FinePositionResolver> finePosition(final boolean centerIntegers) {
|
||||||
|
return this.wrap(Vec3Argument.vec3(centerIntegers), (result) -> sourceStack -> {
|
||||||
|
final Vec3 vec3 = result.getPosition((CommandSourceStack) sourceStack);
|
||||||
|
|
||||||
|
return MCUtil.toPosition(vec3);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<BlockState> blockState() {
|
||||||
|
return this.wrap(BlockStateArgument.block(PaperCommands.INSTANCE.getBuildContext()), (result) -> {
|
||||||
|
return CraftBlockStates.getBlockState(CraftRegistry.getMinecraftRegistry(), BlockPos.ZERO, result.getState(), result.tag);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<ItemStack> itemStack() {
|
||||||
|
return this.wrap(ItemArgument.item(PaperCommands.INSTANCE.getBuildContext()), (result) -> {
|
||||||
|
return CraftItemStack.asBukkitCopy(result.createItemStack(1, true));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<ItemStackPredicate> itemStackPredicate() {
|
||||||
|
return this.wrap(ItemPredicateArgument.itemPredicate(PaperCommands.INSTANCE.getBuildContext()), type -> itemStack -> type.test(CraftItemStack.asNMSCopy(itemStack)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<NamedTextColor> namedColor() {
|
||||||
|
return this.wrap(ColorArgument.color(), result ->
|
||||||
|
requireNonNull(
|
||||||
|
NamedTextColor.namedColor(
|
||||||
|
requireNonNull(result.getColor(), () -> result + " didn't have a color")
|
||||||
|
),
|
||||||
|
() -> result.getColor() + " didn't map to an adventure named color"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<Component> component() {
|
||||||
|
return this.wrap(ComponentArgument.textComponent(PaperCommands.INSTANCE.getBuildContext()), PaperAdventure::asAdventure);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<Style> style() {
|
||||||
|
return this.wrap(StyleArgument.style(PaperCommands.INSTANCE.getBuildContext()), PaperAdventure::asAdventure);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<SignedMessageResolver> signedMessage() {
|
||||||
|
return this.wrap(MessageArgument.message(), SignedMessageResolverImpl::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<DisplaySlot> scoreboardDisplaySlot() {
|
||||||
|
return this.wrap(ScoreboardSlotArgument.displaySlot(), CraftScoreboardTranslations::toBukkitSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<NamespacedKey> namespacedKey() {
|
||||||
|
return this.wrap(ResourceLocationArgument.id(), CraftNamespacedKey::fromMinecraft);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<Key> key() {
|
||||||
|
return this.wrap(ResourceLocationArgument.id(), CraftNamespacedKey::fromMinecraft);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<IntegerRangeProvider> integerRange() {
|
||||||
|
return this.wrap(RangeArgument.intRange(), type -> VanillaArgumentProviderImpl.convertToRange(type, integerRange -> () -> integerRange));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<DoubleRangeProvider> doubleRange() {
|
||||||
|
return this.wrap(RangeArgument.floatRange(), type -> VanillaArgumentProviderImpl.convertToRange(type, doubleRange -> () -> doubleRange));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <C extends Number & Comparable<C>, T extends RangeProvider<C>> T convertToRange(final MinMaxBounds<C> bounds, final Function<Range<C>, T> converter) {
|
||||||
|
if (bounds.isAny()) {
|
||||||
|
return converter.apply(Range.all());
|
||||||
|
} else if (bounds.min().isPresent() && bounds.max().isPresent()) {
|
||||||
|
return converter.apply(Range.closed(bounds.min().get(), bounds.max().get()));
|
||||||
|
} else if (bounds.max().isPresent()) {
|
||||||
|
return converter.apply(Range.atMost(bounds.max().get()));
|
||||||
|
} else if (bounds.min().isPresent()) {
|
||||||
|
return converter.apply(Range.atLeast(bounds.min().get()));
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("This is a bug: " + bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<World> world() {
|
||||||
|
return this.wrap(DimensionArgument.dimension(), dimensionLocation -> {
|
||||||
|
// based on DimensionArgument#getDimension
|
||||||
|
final ResourceKey<Level> resourceKey = ResourceKey.create(Registries.DIMENSION, dimensionLocation);
|
||||||
|
final @Nullable ServerLevel serverLevel = MinecraftServer.getServer().getLevel(resourceKey);
|
||||||
|
if (serverLevel == null) {
|
||||||
|
throw DimensionArgument.ERROR_INVALID_VALUE.create(dimensionLocation);
|
||||||
|
} else {
|
||||||
|
return serverLevel.getWorld();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<GameMode> gameMode() {
|
||||||
|
return this.wrap(GameModeArgument.gameMode(), type -> requireNonNull(GameMode.getByValue(type.getId())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<HeightMap> heightMap() {
|
||||||
|
return this.wrap(HeightmapTypeArgument.heightmap(), CraftHeightMap::fromNMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<UUID> uuid() {
|
||||||
|
return this.wrap(UuidArgument.uuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<Criteria> objectiveCriteria() {
|
||||||
|
return this.wrap(ObjectiveCriteriaArgument.criteria(), CraftCriteria::getFromNMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<LookAnchor> entityAnchor() {
|
||||||
|
return this.wrap(EntityAnchorArgument.anchor(), type -> LookAnchor.valueOf(type.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<Integer> time(final int minTicks) {
|
||||||
|
return this.wrap(TimeArgument.time(minTicks));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<Mirror> templateMirror() {
|
||||||
|
return this.wrap(TemplateMirrorArgument.templateMirror(), mirror -> Mirror.valueOf(mirror.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArgumentType<StructureRotation> templateRotation() {
|
||||||
|
return this.wrap(TemplateRotationArgument.templateRotation(), mirror -> StructureRotation.valueOf(mirror.name()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ArgumentType<TypedKey<T>> resourceKey(final RegistryKey<T> registryKey) {
|
||||||
|
return this.wrap(
|
||||||
|
ResourceKeyArgument.key(PaperRegistries.registryToNms(registryKey)),
|
||||||
|
nmsRegistryKey -> TypedKey.create(registryKey, CraftNamespacedKey.fromMinecraft(nmsRegistryKey.location()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ArgumentType<T> resource(final RegistryKey<T> registryKey) {
|
||||||
|
return this.resourceRaw(registryKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes", "UnnecessaryLocalVariable"})
|
||||||
|
private <T, K extends Keyed> ArgumentType<T> resourceRaw(final RegistryKey registryKeyRaw) { // TODO remove Keyed
|
||||||
|
final RegistryKey<K> registryKey = registryKeyRaw;
|
||||||
|
return (ArgumentType<T>) this.wrap(
|
||||||
|
ResourceArgument.resource(PaperCommands.INSTANCE.getBuildContext(), PaperRegistries.registryToNms(registryKey)),
|
||||||
|
resource -> requireNonNull(
|
||||||
|
RegistryAccess.registryAccess()
|
||||||
|
.getRegistry(registryKey)
|
||||||
|
.get(CraftNamespacedKey.fromMinecraft(resource.key().location()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> ArgumentType<T> wrap(final ArgumentType<T> base) {
|
||||||
|
return this.wrap(base, identity -> identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <B, C> ArgumentType<C> wrap(final ArgumentType<B> base, final ResultConverter<B, C> converter) {
|
||||||
|
return new NativeWrapperArgumentType<>(base, converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface ResultConverter<T, R> {
|
||||||
|
|
||||||
|
R convert(T type) throws CommandSyntaxException;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class NativeWrapperArgumentType<M, P> implements ArgumentType<P> {
|
||||||
|
|
||||||
|
private final ArgumentType<M> nmsBase;
|
||||||
|
private final ResultConverter<M, P> converter;
|
||||||
|
|
||||||
|
private NativeWrapperArgumentType(final ArgumentType<M> nmsBase, final ResultConverter<M, P> converter) {
|
||||||
|
this.nmsBase = nmsBase;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgumentType<M> nativeNmsArgumentType() {
|
||||||
|
return this.nmsBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public P parse(final StringReader reader) throws CommandSyntaxException {
|
||||||
|
return this.converter.convert(this.nmsBase.parse(reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
||||||
|
return this.nmsBase.listSuggestions(context, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getExamples() {
|
||||||
|
return this.nmsBase.getExamples();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package io.papermc.paper.command.brigadier.argument;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.Command;
|
||||||
|
import com.mojang.brigadier.RedirectModifier;
|
||||||
|
import com.mojang.brigadier.StringReader;
|
||||||
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
|
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||||
|
import com.mojang.brigadier.context.ParsedArgument;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
||||||
|
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||||
|
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Basically this converts the argument to a different type when parsing.
|
||||||
|
*/
|
||||||
|
public class WrappedArgumentCommandNode<NMS, API> extends ArgumentCommandNode<CommandSourceStack, NMS> {
|
||||||
|
|
||||||
|
private final ArgumentType<API> pureArgumentType;
|
||||||
|
|
||||||
|
public WrappedArgumentCommandNode(
|
||||||
|
final String name,
|
||||||
|
final ArgumentType<API> pureArgumentType,
|
||||||
|
final ArgumentType<NMS> nmsNativeType,
|
||||||
|
final Command<CommandSourceStack> command,
|
||||||
|
final Predicate<CommandSourceStack> requirement,
|
||||||
|
final CommandNode<CommandSourceStack> redirect,
|
||||||
|
final RedirectModifier<CommandSourceStack> modifier,
|
||||||
|
final boolean forks,
|
||||||
|
final SuggestionProvider<CommandSourceStack> customSuggestions
|
||||||
|
) {
|
||||||
|
super(name, nmsNativeType, command, requirement, redirect, modifier, forks, customSuggestions);
|
||||||
|
if (!ArgumentTypeInfos.isClassRecognized(nmsNativeType.getClass())) {
|
||||||
|
// Is this argument an NMS argument?
|
||||||
|
throw new IllegalArgumentException("Unexpected argument type was passed: " + nmsNativeType.getClass() + ". This should be an NMS type!");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pureArgumentType = pureArgumentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See ArgumentCommandNode#parse
|
||||||
|
@Override
|
||||||
|
public void parse(final StringReader reader, final CommandContextBuilder<CommandSourceStack> contextBuilder) throws CommandSyntaxException {
|
||||||
|
final int start = reader.getCursor();
|
||||||
|
final API result = this.pureArgumentType.parse(reader); // Use the api argument parser
|
||||||
|
final ParsedArgument<CommandSourceStack, API> parsed = new ParsedArgument<>(start, reader.getCursor(), result); // Return an API parsed argument instead.
|
||||||
|
|
||||||
|
contextBuilder.withArgument(this.getName(), parsed);
|
||||||
|
contextBuilder.withNode(this, parsed.getRange());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,338 @@
|
||||||
|
package io.papermc.paper.command.brigadier.bukkit;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterators;
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
|
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||||
|
import io.papermc.paper.command.brigadier.PaperBrigadier;
|
||||||
|
import io.papermc.paper.command.brigadier.PaperCommands;
|
||||||
|
import io.papermc.paper.command.brigadier.PluginVanillaCommandWrapper;
|
||||||
|
import java.util.AbstractCollection;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This map is supposed to act as a legacy bridge for the command map and the command dispatcher.
|
||||||
|
*/
|
||||||
|
public class BukkitBrigForwardingMap extends HashMap<String, Command> {
|
||||||
|
|
||||||
|
public static BukkitBrigForwardingMap INSTANCE = new BukkitBrigForwardingMap();
|
||||||
|
|
||||||
|
private final EntrySet entrySet = new EntrySet();
|
||||||
|
private final KeySet keySet = new KeySet();
|
||||||
|
private final Values values = new Values();
|
||||||
|
|
||||||
|
// Previous dispatcher used to get commands to migrate to another dispatcher
|
||||||
|
|
||||||
|
public CommandDispatcher<CommandSourceStack> getDispatcher() {
|
||||||
|
return PaperCommands.INSTANCE.getDispatcherInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return this.getDispatcher().getRoot().getChildren().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.size() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
if (!(key instanceof String stringKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do any children match?
|
||||||
|
return this.getDispatcher().getRoot().getChild(stringKey) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(@Nullable final Object value) {
|
||||||
|
if (!(value instanceof Command)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CommandNode<CommandSourceStack> child : this.getDispatcher().getRoot().getChildren()) {
|
||||||
|
// If child is a bukkit command node, we can convert it!
|
||||||
|
if (child instanceof BukkitCommandNode bukkitCommandNode) {
|
||||||
|
return bukkitCommandNode.getBukkitCommand().equals(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command get(Object key) {
|
||||||
|
CommandNode<?> node = this.getDispatcher().getRoot().getChild((String) key);
|
||||||
|
if (node == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node instanceof BukkitCommandNode bukkitCommandNode) {
|
||||||
|
return bukkitCommandNode.getBukkitCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
return PaperBrigadier.wrapNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Command put(String key, Command value) {
|
||||||
|
Command old = this.get(key);
|
||||||
|
this.getDispatcher().getRoot().removeCommand(key); // Override previous command
|
||||||
|
if (value instanceof PluginVanillaCommandWrapper wrapper && wrapper.getName().equals(key)) {
|
||||||
|
// Don't break when some plugin tries to remove and add back a plugin command registered with modern API...
|
||||||
|
this.getDispatcher().getRoot().addChild((CommandNode) wrapper.vanillaCommand);
|
||||||
|
} else {
|
||||||
|
this.getDispatcher().getRoot().addChild(BukkitCommandNode.of(key, value));
|
||||||
|
}
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command remove(Object key) {
|
||||||
|
if (!(key instanceof String string)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Command old = this.get(key);
|
||||||
|
if (old != null) {
|
||||||
|
this.getDispatcher().getRoot().removeCommand(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object key, Object value) {
|
||||||
|
Command old = this.get(key);
|
||||||
|
if (Objects.equals(old, value)) {
|
||||||
|
this.remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(@NotNull Map<? extends String, ? extends Command> m) {
|
||||||
|
for (Entry<? extends String, ? extends Command> entry : m.entrySet()) {
|
||||||
|
this.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
this.getDispatcher().getRoot().clearAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<String> keySet() {
|
||||||
|
return this.keySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<Command> values() {
|
||||||
|
return this.values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<Entry<String, Command>> entrySet() {
|
||||||
|
return this.entrySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Values extends AbstractCollection<Command> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Command> iterator() {
|
||||||
|
// AVOID CME since commands can modify multiple commands now through alises, which means it may appear in the iterator even if removed.
|
||||||
|
// Oh well!
|
||||||
|
Iterator<CommandNode<CommandSourceStack>> iterator = new ArrayList<>(BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren()).iterator();
|
||||||
|
|
||||||
|
return new Iterator<>() {
|
||||||
|
|
||||||
|
private CommandNode<CommandSourceStack> lastFetched;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
if (this.lastFetched == null) {
|
||||||
|
throw new IllegalStateException("next not yet called");
|
||||||
|
}
|
||||||
|
|
||||||
|
BukkitBrigForwardingMap.this.remove(this.lastFetched.getName());
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return iterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command next() {
|
||||||
|
CommandNode<CommandSourceStack> next = iterator.next();
|
||||||
|
this.lastFetched = next;
|
||||||
|
if (next instanceof BukkitCommandNode bukkitCommandNode) {
|
||||||
|
return bukkitCommandNode.getBukkitCommand();
|
||||||
|
} else {
|
||||||
|
return PaperBrigadier.wrapNode(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
BukkitBrigForwardingMap.this.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final class KeySet extends AbstractSet<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return BukkitBrigForwardingMap.this.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
BukkitBrigForwardingMap.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<String> iterator() {
|
||||||
|
return Iterators.transform(BukkitBrigForwardingMap.this.values.iterator(), Command::getName); // Wrap around the values iterator for consistancy
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return BukkitBrigForwardingMap.this.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
return BukkitBrigForwardingMap.this.remove(o) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spliterator<String> spliterator() {
|
||||||
|
return this.entryStream().spliterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEach(Consumer<? super String> action) {
|
||||||
|
this.entryStream().forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<String> entryStream() {
|
||||||
|
return BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class EntrySet extends AbstractSet<Entry<String, Command>> {
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return BukkitBrigForwardingMap.this.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
BukkitBrigForwardingMap.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<String, Command>> iterator() {
|
||||||
|
return this.entryStream().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
if (!(o instanceof Map.Entry<?, ?> entry)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object key = entry.getKey();
|
||||||
|
Command candidate = get(key);
|
||||||
|
return candidate != null && candidate.equals(entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
if (o instanceof Map.Entry<?, ?> e) {
|
||||||
|
Object key = e.getKey();
|
||||||
|
Object value = e.getValue();
|
||||||
|
return BukkitBrigForwardingMap.this.remove(key, value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spliterator<Entry<String, Command>> spliterator() {
|
||||||
|
return this.entryStream().spliterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEach(Consumer<? super Entry<String, Command>> action) {
|
||||||
|
this.entryStream().forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<Map.Entry<String, Command>> entryStream() {
|
||||||
|
return BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren().stream().map(BukkitBrigForwardingMap.this::nodeToEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map.Entry<String, Command> nodeToEntry(CommandNode<?> node) {
|
||||||
|
if (node instanceof BukkitCommandNode bukkitCommandNode) {
|
||||||
|
return this.mutableEntry(bukkitCommandNode.getName(), bukkitCommandNode.getBukkitCommand());
|
||||||
|
} else {
|
||||||
|
Command wrapped = PaperBrigadier.wrapNode(node);
|
||||||
|
return this.mutableEntry(node.getName(), wrapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map.Entry<String, Command> mutableEntry(String key, Command command) {
|
||||||
|
return new Entry<>() {
|
||||||
|
@Override
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command getValue() {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command setValue(Command value) {
|
||||||
|
return BukkitBrigForwardingMap.this.put(key, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package io.papermc.paper.command.brigadier.bukkit;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
||||||
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import net.minecraft.commands.CommandSource;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandException;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.server.TabCompleteEvent;
|
||||||
|
|
||||||
|
public class BukkitCommandNode extends LiteralCommandNode<CommandSourceStack> {
|
||||||
|
|
||||||
|
private final Command command;
|
||||||
|
|
||||||
|
private BukkitCommandNode(String literal, Command command, BukkitBrigCommand bukkitBrigCommand) {
|
||||||
|
super(
|
||||||
|
literal, bukkitBrigCommand, source -> {
|
||||||
|
// If the source is null, assume it's true.
|
||||||
|
// As bukkit doesn't really map the command sender well in all cases
|
||||||
|
if (source instanceof net.minecraft.commands.CommandSourceStack commandSourceStack && commandSourceStack.source == CommandSource.NULL) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return command.testPermissionSilent(source.getSender());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null, null, false
|
||||||
|
);
|
||||||
|
this.command = command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BukkitCommandNode of(String name, Command command) {
|
||||||
|
BukkitBrigCommand bukkitBrigCommand = new BukkitBrigCommand(command, name);
|
||||||
|
BukkitCommandNode commandNode = new BukkitCommandNode(name, command, bukkitBrigCommand);
|
||||||
|
commandNode.addChild(
|
||||||
|
RequiredArgumentBuilder.<CommandSourceStack, String>argument("args", StringArgumentType.greedyString())
|
||||||
|
.suggests(new BukkitBrigSuggestionProvider(command, name))
|
||||||
|
.executes(bukkitBrigCommand).build()
|
||||||
|
);
|
||||||
|
|
||||||
|
return commandNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Command getBukkitCommand() {
|
||||||
|
return this.command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BukkitBrigCommand implements com.mojang.brigadier.Command<CommandSourceStack> {
|
||||||
|
|
||||||
|
private final org.bukkit.command.Command command;
|
||||||
|
private final String literal;
|
||||||
|
|
||||||
|
BukkitBrigCommand(org.bukkit.command.Command command, String literal) {
|
||||||
|
this.command = command;
|
||||||
|
this.literal = literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int run(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||||
|
CommandSender sender = context.getSource().getSender();
|
||||||
|
|
||||||
|
String content = context.getRange().get(context.getInput());
|
||||||
|
String[] args = org.apache.commons.lang3.StringUtils.split(content, ' '); // fix adjacent spaces (from console/plugins) causing empty array elements
|
||||||
|
|
||||||
|
// Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false)
|
||||||
|
this.command.execute(sender, this.literal, Arrays.copyOfRange(args, 1, args.length));
|
||||||
|
|
||||||
|
// return true as command was handled
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BukkitBrigSuggestionProvider implements SuggestionProvider<CommandSourceStack> {
|
||||||
|
|
||||||
|
private final org.bukkit.command.Command command;
|
||||||
|
private final String literal;
|
||||||
|
|
||||||
|
BukkitBrigSuggestionProvider(org.bukkit.command.Command command, String literal) {
|
||||||
|
this.command = command;
|
||||||
|
this.literal = literal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Suggestions> getSuggestions(CommandContext<CommandSourceStack> context, SuggestionsBuilder builder) throws CommandSyntaxException {
|
||||||
|
// Paper start
|
||||||
|
org.bukkit.command.CommandSender sender = context.getSource().getSender();
|
||||||
|
String[] args = builder.getRemaining().split(" ", -1); // We need the command included -- Set limit to -1, allow for trailing spaces
|
||||||
|
|
||||||
|
List<String> results = null;
|
||||||
|
Location pos = context.getSource().getLocation();
|
||||||
|
try {
|
||||||
|
results = this.command.tabComplete(sender, this.literal, args, pos.clone());
|
||||||
|
} catch (CommandException ex) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command");
|
||||||
|
Bukkit.getServer().getLogger().log(Level.SEVERE, "Exception when " + sender.getName() + " attempted to tab complete " + builder.getRemaining(), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sender instanceof final Player player) {
|
||||||
|
TabCompleteEvent tabEvent = new org.bukkit.event.server.TabCompleteEvent(player, builder.getInput(), results != null ? results : new ArrayList<>(), true, pos); // Paper - AsyncTabCompleteEvent
|
||||||
|
if (!tabEvent.callEvent()) {
|
||||||
|
results = null;
|
||||||
|
} else {
|
||||||
|
results = tabEvent.getCompletions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
if (results == null) {
|
||||||
|
return builder.buildFuture();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaults to sub nodes, but we have just one giant args node, so offset accordingly
|
||||||
|
builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
|
||||||
|
|
||||||
|
for (String s : results) {
|
||||||
|
builder.suggest(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.buildFuture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -275,11 +275,11 @@ public final class CraftServer implements Server {
|
||||||
private final Logger logger = Logger.getLogger("Minecraft");
|
private final Logger logger = Logger.getLogger("Minecraft");
|
||||||
private final ServicesManager servicesManager = new SimpleServicesManager();
|
private final ServicesManager servicesManager = new SimpleServicesManager();
|
||||||
private final CraftScheduler scheduler = new CraftScheduler();
|
private final CraftScheduler scheduler = new CraftScheduler();
|
||||||
private final CraftCommandMap commandMap = new CraftCommandMap(this);
|
private final CraftCommandMap commandMap; // Paper - Move down
|
||||||
private final SimpleHelpMap helpMap = new SimpleHelpMap(this);
|
private final SimpleHelpMap helpMap = new SimpleHelpMap(this);
|
||||||
private final StandardMessenger messenger = new StandardMessenger();
|
private final StandardMessenger messenger = new StandardMessenger();
|
||||||
private final SimplePluginManager pluginManager = new SimplePluginManager(this, commandMap);
|
private final SimplePluginManager pluginManager; // Paper - Move down
|
||||||
public final io.papermc.paper.plugin.manager.PaperPluginManagerImpl paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager); {this.pluginManager.paperPluginManager = this.paperPluginManager;} // Paper
|
public final io.papermc.paper.plugin.manager.PaperPluginManagerImpl paperPluginManager; // Paper
|
||||||
private final StructureManager structureManager;
|
private final StructureManager structureManager;
|
||||||
protected final DedicatedServer console;
|
protected final DedicatedServer console;
|
||||||
protected final DedicatedPlayerList playerList;
|
protected final DedicatedPlayerList playerList;
|
||||||
|
@ -419,6 +419,12 @@ public final class CraftServer implements Server {
|
||||||
this.serverLinks = new CraftServerLinks(console);
|
this.serverLinks = new CraftServerLinks(console);
|
||||||
|
|
||||||
Bukkit.setServer(this);
|
Bukkit.setServer(this);
|
||||||
|
// Paper start
|
||||||
|
this.commandMap = new CraftCommandMap(this);
|
||||||
|
this.pluginManager = new SimplePluginManager(this, commandMap);
|
||||||
|
this.paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager);
|
||||||
|
this.pluginManager.paperPluginManager = this.paperPluginManager;
|
||||||
|
// Paper end
|
||||||
|
|
||||||
CraftRegistry.setMinecraftRegistry(console.registryAccess());
|
CraftRegistry.setMinecraftRegistry(console.registryAccess());
|
||||||
|
|
||||||
|
@ -617,48 +623,11 @@ public final class CraftServer implements Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setVanillaCommands(boolean first) { // Spigot
|
private void setVanillaCommands(boolean first) { // Spigot
|
||||||
Commands dispatcher = this.console.vanillaCommandDispatcher;
|
// Paper - Replace implementation
|
||||||
|
|
||||||
// Build a list of all Vanilla commands and create wrappers
|
|
||||||
for (CommandNode<CommandSourceStack> cmd : dispatcher.getDispatcher().getRoot().getChildren()) {
|
|
||||||
// Spigot start
|
|
||||||
VanillaCommandWrapper wrapper = new VanillaCommandWrapper(dispatcher, cmd);
|
|
||||||
if (org.spigotmc.SpigotConfig.replaceCommands.contains( wrapper.getName() ) ) {
|
|
||||||
if (first) {
|
|
||||||
this.commandMap.register("minecraft", wrapper);
|
|
||||||
}
|
|
||||||
} else if (!first) {
|
|
||||||
this.commandMap.register("minecraft", wrapper);
|
|
||||||
}
|
|
||||||
// Spigot end
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void syncCommands() {
|
public void syncCommands() {
|
||||||
// Clear existing commands
|
Commands dispatcher = this.getHandle().getServer().getCommands(); // Paper - We now register directly to the dispatcher.
|
||||||
Commands dispatcher = this.console.resources.managers().commands = new Commands();
|
|
||||||
|
|
||||||
// Register all commands, vanilla ones will be using the old dispatcher references
|
|
||||||
for (Map.Entry<String, Command> entry : this.commandMap.getKnownCommands().entrySet()) {
|
|
||||||
String label = entry.getKey();
|
|
||||||
Command command = entry.getValue();
|
|
||||||
|
|
||||||
if (command instanceof VanillaCommandWrapper) {
|
|
||||||
LiteralCommandNode<CommandSourceStack> node = (LiteralCommandNode<CommandSourceStack>) ((VanillaCommandWrapper) command).vanillaCommand;
|
|
||||||
if (!node.getLiteral().equals(label)) {
|
|
||||||
LiteralCommandNode<CommandSourceStack> clone = new LiteralCommandNode(label, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork());
|
|
||||||
|
|
||||||
for (CommandNode<CommandSourceStack> child : node.getChildren()) {
|
|
||||||
clone.addChild(child);
|
|
||||||
}
|
|
||||||
node = clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatcher.getDispatcher().getRoot().addChild(node);
|
|
||||||
} else {
|
|
||||||
new BukkitCommandWrapper(this, entry.getValue()).register(dispatcher.getDispatcher(), label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh commands
|
// Refresh commands
|
||||||
for (ServerPlayer player : this.getHandle().players) {
|
for (ServerPlayer player : this.getHandle().players) {
|
||||||
|
@ -1045,17 +1014,31 @@ public final class CraftServer implements Server {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spigot start
|
return this.dispatchCommand(VanillaCommandWrapper.getListener(sender), commandLine);
|
||||||
if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) {
|
}
|
||||||
// Paper start
|
|
||||||
org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(sender, commandLine, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.unknownCommandMessage));
|
public boolean dispatchCommand(CommandSourceStack sourceStack, String commandLine) {
|
||||||
this.getPluginManager().callEvent(event);
|
net.minecraft.commands.Commands commands = this.getHandle().getServer().getCommands();
|
||||||
if (event.message() != null) {
|
com.mojang.brigadier.CommandDispatcher<CommandSourceStack> dispatcher = commands.getDispatcher();
|
||||||
sender.sendMessage(event.message());
|
com.mojang.brigadier.ParseResults<CommandSourceStack> results = dispatcher.parse(commandLine, sourceStack);
|
||||||
}
|
|
||||||
// Paper end
|
CommandSender sender = sourceStack.getBukkitSender();
|
||||||
|
String[] args = org.apache.commons.lang3.StringUtils.split(commandLine, ' '); // Paper - fix adjacent spaces (from console/plugins) causing empty array elements
|
||||||
|
Command target = this.commandMap.getCommand(args[0].toLowerCase(java.util.Locale.ENGLISH));
|
||||||
|
|
||||||
|
try {
|
||||||
|
commands.performCommand(results, commandLine, commandLine, true);
|
||||||
|
} catch (CommandException ex) {
|
||||||
|
this.pluginManager.callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper
|
||||||
|
//target.timings.stopTiming(); // Spigot // Paper
|
||||||
|
throw ex;
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
//target.timings.stopTiming(); // Spigot // Paper
|
||||||
|
String msg = "Unhandled exception executing '" + commandLine + "' in " + target;
|
||||||
|
this.pluginManager.callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper
|
||||||
|
throw new CommandException(msg, ex);
|
||||||
}
|
}
|
||||||
// Spigot end
|
// Paper end
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1064,7 +1047,7 @@ public final class CraftServer implements Server {
|
||||||
public void reload() {
|
public void reload() {
|
||||||
// Paper start - lifecycle events
|
// Paper start - lifecycle events
|
||||||
if (io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.blocksPluginReloading()) {
|
if (io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.blocksPluginReloading()) {
|
||||||
throw new IllegalStateException("A lifecycle event handler has been registered which makes reloading plugins not possible");
|
throw new IllegalStateException(org.bukkit.command.defaults.ReloadCommand.RELOADING_DISABLED_MESSAGE);
|
||||||
}
|
}
|
||||||
// Paper end - lifecycle events
|
// Paper end - lifecycle events
|
||||||
org.spigotmc.WatchdogThread.hasStarted = false; // Paper - Disable watchdog early timeout on reload
|
org.spigotmc.WatchdogThread.hasStarted = false; // Paper - Disable watchdog early timeout on reload
|
||||||
|
@ -1119,8 +1102,9 @@ public final class CraftServer implements Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
|
Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
|
||||||
|
this.commandMap.clearCommands(); // Paper - Move command reloading up
|
||||||
this.pluginManager.clearPlugins();
|
this.pluginManager.clearPlugins();
|
||||||
this.commandMap.clearCommands();
|
// Paper - move up
|
||||||
// Paper start
|
// Paper start
|
||||||
for (Plugin plugin : pluginClone) {
|
for (Plugin plugin : pluginClone) {
|
||||||
entityMetadata.removeAll(plugin);
|
entityMetadata.removeAll(plugin);
|
||||||
|
@ -1160,6 +1144,12 @@ public final class CraftServer implements Server {
|
||||||
this.enablePlugins(PluginLoadOrder.STARTUP);
|
this.enablePlugins(PluginLoadOrder.STARTUP);
|
||||||
this.enablePlugins(PluginLoadOrder.POSTWORLD);
|
this.enablePlugins(PluginLoadOrder.POSTWORLD);
|
||||||
if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
|
if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
|
||||||
|
// Paper start - brigadier command API
|
||||||
|
io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // to clear invalid state for event fire below
|
||||||
|
io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // call commands event for regular plugins
|
||||||
|
this.helpMap.initializeCommands();
|
||||||
|
this.syncCommands(); // Refresh commands after event
|
||||||
|
// Paper end - brigadier command API
|
||||||
this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
|
this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
|
||||||
org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Disable watchdog early timeout on reload
|
org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Disable watchdog early timeout on reload
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.bukkit.command.CommandException;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.craftbukkit.CraftServer;
|
import org.bukkit.craftbukkit.CraftServer;
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true) // Paper - Don't use
|
||||||
public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandSourceStack>, Predicate<CommandSourceStack>, SuggestionProvider<CommandSourceStack>, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand<CommandSourceStack> { // Paper
|
public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandSourceStack>, Predicate<CommandSourceStack>, SuggestionProvider<CommandSourceStack>, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand<CommandSourceStack> { // Paper
|
||||||
|
|
||||||
private final CraftServer server;
|
private final CraftServer server;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import org.bukkit.command.SimpleCommandMap;
|
||||||
public class CraftCommandMap extends SimpleCommandMap {
|
public class CraftCommandMap extends SimpleCommandMap {
|
||||||
|
|
||||||
public CraftCommandMap(Server server) {
|
public CraftCommandMap(Server server) {
|
||||||
super(server);
|
super(server, io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Command> getKnownCommands() {
|
public Map<String, Command> getKnownCommands() {
|
||||||
|
|
|
@ -24,14 +24,26 @@ import org.bukkit.craftbukkit.entity.CraftMinecartCommand;
|
||||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||||
import org.bukkit.entity.minecart.CommandMinecart;
|
import org.bukkit.entity.minecart.CommandMinecart;
|
||||||
|
|
||||||
public final class VanillaCommandWrapper extends BukkitCommand {
|
public class VanillaCommandWrapper extends BukkitCommand { // Paper
|
||||||
|
|
||||||
private final Commands dispatcher;
|
//private final Commands dispatcher; // Paper
|
||||||
public final CommandNode<CommandSourceStack> vanillaCommand;
|
public final CommandNode<CommandSourceStack> vanillaCommand;
|
||||||
|
|
||||||
|
// Paper start
|
||||||
|
public VanillaCommandWrapper(String name, String description, String usageMessage, List<String> aliases, CommandNode<CommandSourceStack> vanillaCommand) {
|
||||||
|
super(name, description, usageMessage, aliases);
|
||||||
|
//this.dispatcher = dispatcher; // Paper
|
||||||
|
this.vanillaCommand = vanillaCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
Commands commands() {
|
||||||
|
return net.minecraft.server.MinecraftServer.getServer().getCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper end
|
||||||
public VanillaCommandWrapper(Commands dispatcher, CommandNode<CommandSourceStack> vanillaCommand) {
|
public VanillaCommandWrapper(Commands dispatcher, CommandNode<CommandSourceStack> vanillaCommand) {
|
||||||
super(vanillaCommand.getName(), "A Mojang provided command.", vanillaCommand.getUsageText(), Collections.EMPTY_LIST);
|
super(vanillaCommand.getName(), "A Mojang provided command.", vanillaCommand.getUsageText(), Collections.EMPTY_LIST);
|
||||||
this.dispatcher = dispatcher;
|
// this.dispatcher = dispatcher; // Paper
|
||||||
this.vanillaCommand = vanillaCommand;
|
this.vanillaCommand = vanillaCommand;
|
||||||
this.setPermission(VanillaCommandWrapper.getPermission(vanillaCommand));
|
this.setPermission(VanillaCommandWrapper.getPermission(vanillaCommand));
|
||||||
}
|
}
|
||||||
|
@ -41,7 +53,7 @@ public final class VanillaCommandWrapper extends BukkitCommand {
|
||||||
if (!this.testPermission(sender)) return true;
|
if (!this.testPermission(sender)) return true;
|
||||||
|
|
||||||
CommandSourceStack icommandlistener = VanillaCommandWrapper.getListener(sender);
|
CommandSourceStack icommandlistener = VanillaCommandWrapper.getListener(sender);
|
||||||
this.dispatcher.performPrefixedCommand(icommandlistener, this.toDispatcher(args, this.getName()), this.toDispatcher(args, commandLabel));
|
this.commands().performPrefixedCommand(icommandlistener, this.toDispatcher(args, this.getName()), this.toDispatcher(args, commandLabel)); // Paper
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,10 +64,10 @@ public final class VanillaCommandWrapper extends BukkitCommand {
|
||||||
Preconditions.checkArgument(alias != null, "Alias cannot be null");
|
Preconditions.checkArgument(alias != null, "Alias cannot be null");
|
||||||
|
|
||||||
CommandSourceStack icommandlistener = VanillaCommandWrapper.getListener(sender);
|
CommandSourceStack icommandlistener = VanillaCommandWrapper.getListener(sender);
|
||||||
ParseResults<CommandSourceStack> parsed = this.dispatcher.getDispatcher().parse(this.toDispatcher(args, this.getName()), icommandlistener);
|
ParseResults<CommandSourceStack> parsed = this.commands().getDispatcher().parse(this.toDispatcher(args, this.getName()), icommandlistener); // Paper
|
||||||
|
|
||||||
List<String> results = new ArrayList<>();
|
List<String> results = new ArrayList<>();
|
||||||
this.dispatcher.getDispatcher().getCompletionSuggestions(parsed).thenAccept((suggestions) -> {
|
this.commands().getDispatcher().getCompletionSuggestions(parsed).thenAccept((suggestions) -> { // Paper
|
||||||
suggestions.getList().forEach((s) -> results.add(s.getText()));
|
suggestions.getList().forEach((s) -> results.add(s.getText()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -116,4 +128,15 @@ public final class VanillaCommandWrapper extends BukkitCommand {
|
||||||
private String toDispatcher(String[] args, String name) {
|
private String toDispatcher(String[] args, String name) {
|
||||||
return name + ((args.length > 0) ? " " + Joiner.on(' ').join(args) : "");
|
return name + ((args.length > 0) ? " " + Joiner.on(' ').join(args) : "");
|
||||||
}
|
}
|
||||||
|
// Paper start
|
||||||
|
@Override
|
||||||
|
public boolean canBeOverriden() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRegistered() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,15 +200,18 @@ public class SimpleHelpMap implements HelpMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCommandPluginName(Command command) {
|
private String getCommandPluginName(Command command) {
|
||||||
|
// Paper start - Move up
|
||||||
|
if (command instanceof PluginIdentifiableCommand) {
|
||||||
|
return ((PluginIdentifiableCommand) command).getPlugin().getName();
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
if (command instanceof VanillaCommandWrapper) {
|
if (command instanceof VanillaCommandWrapper) {
|
||||||
return "Minecraft";
|
return "Minecraft";
|
||||||
}
|
}
|
||||||
if (command instanceof BukkitCommand) {
|
if (command instanceof BukkitCommand) {
|
||||||
return "Bukkit";
|
return "Bukkit";
|
||||||
}
|
}
|
||||||
if (command instanceof PluginIdentifiableCommand) {
|
// Paper - Move PluginIdentifiableCommand instanceof check to allow brig commands
|
||||||
return ((PluginIdentifiableCommand) command).getPlugin().getName();
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,13 @@ public final class CraftCriteria implements Criteria {
|
||||||
return RenderType.values()[this.criteria.getDefaultRenderType().ordinal()];
|
return RenderType.values()[this.criteria.getDefaultRenderType().ordinal()];
|
||||||
}
|
}
|
||||||
|
|
||||||
static CraftCriteria getFromNMS(Objective objective) {
|
// Paper start
|
||||||
|
public static CraftCriteria getFromNMS(ObjectiveCriteria criteria) {
|
||||||
|
return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(criteria.getName()), () -> new CraftCriteria(criteria));
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
public static CraftCriteria getFromNMS(Objective objective) {
|
||||||
return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(objective.getCriteria().getName()), () -> new CraftCriteria(objective.getCriteria())); // Paper
|
return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(objective.getCriteria().getName()), () -> new CraftCriteria(objective.getCriteria())); // Paper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
io.papermc.paper.command.brigadier.CommandBuilderImpl$ProviderImpl
|
|
@ -0,0 +1 @@
|
||||||
|
io.papermc.paper.command.brigadier.MessageComponentSerializerImpl
|
|
@ -0,0 +1 @@
|
||||||
|
io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl
|
|
@ -0,0 +1,102 @@
|
||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
|
import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandMap;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.SimpleCommandMap;
|
||||||
|
import org.bukkit.support.RegistryHelper;
|
||||||
|
import org.bukkit.support.environment.AllFeatures;
|
||||||
|
import org.bukkit.support.environment.Normal;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
@Normal
|
||||||
|
public class BukkitCommandConversionTest {
|
||||||
|
|
||||||
|
private CommandSender getSender() {
|
||||||
|
return Mockito.mock(CommandSender.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws CommandSyntaxException {
|
||||||
|
CommandSender sender = this.getSender();
|
||||||
|
CommandSourceStack object = Mockito.mock(CommandSourceStack.class);
|
||||||
|
Mockito.when(object.getLocation()).thenReturn(new Location(null, 0, 0, 0));;
|
||||||
|
|
||||||
|
CommandDispatcher dispatcher = RegistryHelper.getDataPack().commands.getDispatcher();
|
||||||
|
dispatcher.setConsumer((context, success, result) -> {});
|
||||||
|
CommandMap commandMap = new SimpleCommandMap(Bukkit.getServer(), new BukkitBrigForwardingMap());
|
||||||
|
Map<String, Command> stringCommandMap = commandMap.getKnownCommands();
|
||||||
|
// All commands should be mirrored -- or equal
|
||||||
|
int commandMapSize = stringCommandMap.values().size();
|
||||||
|
ExampleCommand exampleCommand = new ExampleCommand();
|
||||||
|
|
||||||
|
Assertions.assertEquals(commandMapSize, dispatcher.getRoot().getChildren().size());
|
||||||
|
|
||||||
|
// Register a new command
|
||||||
|
commandMap.register("test", exampleCommand);
|
||||||
|
Assertions.assertEquals(commandMapSize + (3 * 2), stringCommandMap.values().size()); // Make sure commands are accounted for, including those with namespaced keys
|
||||||
|
|
||||||
|
// Test Registration
|
||||||
|
for (String alias : exampleCommand.getAliases()) {
|
||||||
|
Assertions.assertEquals(stringCommandMap.get(alias), exampleCommand);
|
||||||
|
Assertions.assertEquals(stringCommandMap.get("test:" + alias), exampleCommand);
|
||||||
|
}
|
||||||
|
// Test command instance equality
|
||||||
|
Assertions.assertEquals(stringCommandMap.get(exampleCommand.getName()), exampleCommand);
|
||||||
|
Assertions.assertEquals(stringCommandMap.get("test:" + exampleCommand.getName()), exampleCommand);
|
||||||
|
|
||||||
|
// Test command map execution
|
||||||
|
commandMap.dispatch(sender, "main-example example");
|
||||||
|
Assertions.assertEquals(exampleCommand.invocations, 1);
|
||||||
|
Assertions.assertEquals(commandMap.tabComplete(sender, "main-example 1 2"), List.of("complete"));
|
||||||
|
|
||||||
|
// Test dispatcher execution
|
||||||
|
dispatcher.execute("main-example example", object);
|
||||||
|
Assertions.assertEquals(exampleCommand.invocations, 2);
|
||||||
|
|
||||||
|
dispatcher.execute("test:example2 example", object);
|
||||||
|
Assertions.assertEquals(exampleCommand.invocations, 3);
|
||||||
|
|
||||||
|
Suggestions suggestions = (Suggestions) dispatcher.getCompletionSuggestions(dispatcher.parse("main-example 1 2", object)).join();
|
||||||
|
Assertions.assertEquals(suggestions.getList().get(0).getText(), "complete");
|
||||||
|
|
||||||
|
|
||||||
|
// Test command map removal
|
||||||
|
commandMap.getKnownCommands().remove("test");
|
||||||
|
Assertions.assertNull(commandMap.getCommand("test"));
|
||||||
|
Assertions.assertNull(dispatcher.getRoot().getChild("test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExampleCommand extends Command {
|
||||||
|
|
||||||
|
int invocations;
|
||||||
|
|
||||||
|
protected ExampleCommand() {
|
||||||
|
super("main-example", "This is an example.", "", List.of("example", "example2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
||||||
|
Assertions.assertEquals(args[0], "example");
|
||||||
|
this.invocations++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
|
||||||
|
Assertions.assertEquals(args.length, 2);
|
||||||
|
return List.of("complete");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -87,7 +87,7 @@ public final class DummyServerHelper {
|
||||||
// Paper start - testing additions
|
// Paper start - testing additions
|
||||||
final Thread currentThread = Thread.currentThread();
|
final Thread currentThread = Thread.currentThread();
|
||||||
when(instance.isPrimaryThread()).thenAnswer(ignored -> Thread.currentThread().equals(currentThread));
|
when(instance.isPrimaryThread()).thenAnswer(ignored -> Thread.currentThread().equals(currentThread));
|
||||||
final org.bukkit.plugin.PluginManager pluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(instance, new org.bukkit.command.SimpleCommandMap(instance), null);
|
final org.bukkit.plugin.PluginManager pluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(instance, new org.bukkit.command.SimpleCommandMap(instance, new java.util.HashMap<>()), null);
|
||||||
when(instance.getPluginManager()).thenReturn(pluginManager);
|
when(instance.getPluginManager()).thenReturn(pluginManager);
|
||||||
// Paper end - testing additions
|
// Paper end - testing additions
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue