diff --git a/paper-server/patches/unapplied/net/minecraft/commands/CommandSource.java.patch b/paper-server/patches/sources/net/minecraft/commands/CommandSource.java.patch similarity index 84% rename from paper-server/patches/unapplied/net/minecraft/commands/CommandSource.java.patch rename to paper-server/patches/sources/net/minecraft/commands/CommandSource.java.patch index 4d74948c36..c541c1a047 100644 --- a/paper-server/patches/unapplied/net/minecraft/commands/CommandSource.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/CommandSource.java.patch @@ -1,20 +1,20 @@ --- a/net/minecraft/commands/CommandSource.java +++ b/net/minecraft/commands/CommandSource.java -@@ -22,6 +22,13 @@ +@@ -22,6 +_,13 @@ public boolean shouldInformAdmins() { return false; } + + // CraftBukkit start + @Override -+ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) { ++ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack stack) { + return io.papermc.paper.brigadier.NullCommandSender.INSTANCE; // Paper - expose a no-op CommandSender + } + // CraftBukkit end }; - void sendSystemMessage(Component message); -@@ -35,4 +42,6 @@ + void sendSystemMessage(Component component); +@@ -35,4 +_,6 @@ default boolean alwaysAccepts() { return false; } diff --git a/paper-server/patches/unapplied/net/minecraft/commands/CommandSourceStack.java.patch b/paper-server/patches/sources/net/minecraft/commands/CommandSourceStack.java.patch similarity index 77% rename from paper-server/patches/unapplied/net/minecraft/commands/CommandSourceStack.java.patch rename to paper-server/patches/sources/net/minecraft/commands/CommandSourceStack.java.patch index 94ee38720d..b8593758f7 100644 --- a/paper-server/patches/unapplied/net/minecraft/commands/CommandSourceStack.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/CommandSourceStack.java.patch @@ -1,33 +1,30 @@ --- a/net/minecraft/commands/CommandSourceStack.java +++ b/net/minecraft/commands/CommandSourceStack.java -@@ -45,9 +45,9 @@ - import net.minecraft.world.level.dimension.DimensionType; +@@ -45,7 +_,7 @@ import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; -+import com.mojang.brigadier.tree.CommandNode; // CraftBukkit -public class CommandSourceStack implements ExecutionCommandSource, SharedSuggestionProvider { -- +public class CommandSourceStack implements ExecutionCommandSource, 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_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity")); public final CommandSource source; -@@ -65,6 +65,8 @@ +@@ -63,6 +_,8 @@ private final Vec2 rotation; private final CommandSigningContext signingContext; private final TaskChainer chatMessageChainer; -+ public java.util.Map currentCommand = new java.util.concurrent.ConcurrentHashMap<>(); // CraftBukkit // Paper - Thread Safe Vanilla Command permission checking ++ public java.util.Map currentCommand = new java.util.concurrent.ConcurrentHashMap<>(); // CraftBukkit // Paper - Thread Safe Vanilla Command permission checking + public boolean bypassSelectorPermissions = false; // Paper - add bypass for selector permissions - 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)); -@@ -171,8 +173,43 @@ + public CommandSourceStack( + CommandSource source, +@@ -391,9 +_,44 @@ @Override public boolean hasPermission(int level) { + // CraftBukkit start + // Paper start - Thread Safe Vanilla Command permission checking -+ CommandNode currentCommand = this.currentCommand.get(Thread.currentThread()); ++ com.mojang.brigadier.tree.CommandNode currentCommand = this.currentCommand.get(Thread.currentThread()); + if (currentCommand != null) { + return this.hasPermission(level, org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(currentCommand)); + // Paper end - Thread Safe Vanilla Command permission checking @@ -35,8 +32,8 @@ + // CraftBukkit end + return this.permissionLevel >= level; -+ } -+ + } + + // Paper start - Fix permission levels for command blocks + private boolean forceRespectPermissionLevel() { + return this.source == CommandSource.NULL || (this.source instanceof final net.minecraft.world.level.BaseCommandBlock commandBlock && commandBlock.getLevel().paperConfig().commandBlocks.forceFollowPermLevel); @@ -60,27 +57,27 @@ + } + return hasBukkitPerm.getAsBoolean(); + // Paper end - Fix permission levels for command blocks - } ++ } + // CraftBukkit end - ++ public Vec3 getPosition() { return this.worldPosition; -@@ -302,21 +339,26 @@ - while (iterator.hasNext()) { - ServerPlayer entityplayer = (ServerPlayer) iterator.next(); - -- if (entityplayer.commandSource() != this.source && this.server.getPlayerList().isOp(entityplayer.getGameProfile())) { -+ if (entityplayer.commandSource() != this.source && entityplayer.getBukkitEntity().hasPermission("minecraft.admin.command_feedback")) { // CraftBukkit - entityplayer.sendSystemMessage(ichatmutablecomponent); + } +@@ -499,20 +_,25 @@ + Component component = Component.translatable("chat.type.admin", this.getDisplayName(), message).withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC); + if (this.server.getGameRules().getBoolean(GameRules.RULE_SENDCOMMANDFEEDBACK)) { + for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) { +- if (serverPlayer.commandSource() != this.source && this.server.getPlayerList().isOp(serverPlayer.getGameProfile())) { ++ if (serverPlayer.commandSource() != this.source && serverPlayer.getBukkitEntity().hasPermission("minecraft.admin.command_feedback")) { // CraftBukkit + serverPlayer.sendSystemMessage(component); } } } - if (this.source != this.server && this.server.getGameRules().getBoolean(GameRules.RULE_LOGADMINCOMMANDS)) { + if (this.source != this.server && this.server.getGameRules().getBoolean(GameRules.RULE_LOGADMINCOMMANDS) && !org.spigotmc.SpigotConfig.silentCommandBlocks) { // Spigot - this.server.sendSystemMessage(ichatmutablecomponent); + this.server.sendSystemMessage(component); } - } public void sendFailure(Component message) { @@ -93,9 +90,9 @@ - this.source.sendSystemMessage(Component.empty().append(message).withStyle(ChatFormatting.RED)); + this.source.sendSystemMessage(withStyle ? Component.empty().append(message).withStyle(ChatFormatting.RED) : message); // Paper - Add UnknownCommandEvent } - } -@@ -400,4 +442,16 @@ + +@@ -598,4 +_,16 @@ public boolean isSilent() { return this.silent; } diff --git a/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch b/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch new file mode 100644 index 0000000000..843b1af707 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch @@ -0,0 +1,312 @@ +--- a/net/minecraft/commands/Commands.java ++++ b/net/minecraft/commands/Commands.java +@@ -139,6 +_,14 @@ + import net.minecraft.world.level.GameRules; + import org.slf4j.Logger; + ++// CraftBukkit start ++import com.google.common.base.Joiner; ++import java.util.Collection; ++import java.util.LinkedHashSet; ++import org.bukkit.event.player.PlayerCommandSendEvent; ++import org.bukkit.event.server.ServerCommandEvent; ++// CraftBukkit end ++ + public class Commands { + private static final ThreadLocal> CURRENT_EXECUTION_CONTEXT = new ThreadLocal<>(); + private static final Logger LOGGER = LogUtils.getLogger(); +@@ -251,6 +_,30 @@ + PublishCommand.register(this.dispatcher); + } + ++ // Paper start - Vanilla command permission fixes ++ for (final CommandNode node : this.dispatcher.getRoot().getChildren()) { ++ if (node.getRequirement() == com.mojang.brigadier.builder.ArgumentBuilder.defaultRequirement()) { ++ node.requirement = stack -> stack.source == CommandSource.NULL || stack.getBukkitSender().hasPermission(org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(node)); ++ } ++ } ++ // Paper end - Vanilla command permission fixes ++ // Paper start - Brigadier Command API ++ // Create legacy minecraft namespace commands ++ for (final CommandNode node : new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren())) { ++ // The brigadier dispatcher is not able to resolve nested redirects. ++ // 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 flattenedAliasTarget = node; ++ while (flattenedAliasTarget.getRedirect() != null) flattenedAliasTarget = flattenedAliasTarget.getRedirect(); ++ ++ this.dispatcher.register( ++ com.mojang.brigadier.builder.LiteralArgumentBuilder.literal("minecraft:" + node.getName()) ++ .executes(flattenedAliasTarget.getCommand()) ++ .requires(flattenedAliasTarget.getRequirement()) ++ .redirect(flattenedAliasTarget) ++ ); ++ } ++ // Paper end - Brigadier Command API + this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer()); + } + +@@ -260,15 +_,58 @@ + return new ParseResults<>(commandContextBuilder, parseResults.getReader(), parseResults.getExceptions()); + } + ++ // CraftBukkit start ++ public void dispatchServerCommand(CommandSourceStack sender, String command) { ++ Joiner joiner = Joiner.on(" "); ++ if (command.startsWith("/")) { ++ command = command.substring(1); ++ } ++ ++ ServerCommandEvent event = new ServerCommandEvent(sender.getBukkitSender(), command); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return; ++ } ++ command = event.getCommand(); ++ ++ String[] args = command.split(" "); ++ if (args.length == 0) return; // Paper - empty commands shall not be dispatched ++ ++ // Paper - Fix permission levels for command blocks ++ ++ // Handle vanilla commands; // Paper - handled in CommandNode/CommandDispatcher ++ ++ String newCommand = joiner.join(args); ++ this.performPrefixedCommand(sender, newCommand, newCommand); ++ } ++ // CraftBukkit end ++ + public void performPrefixedCommand(CommandSourceStack source, String command) { ++ // CraftBukkit start ++ this.performPrefixedCommand(source, command, command); ++ } ++ ++ public void performPrefixedCommand(CommandSourceStack source, String command, String label) { + command = command.startsWith("/") ? command.substring(1) : command; +- this.performCommand(this.dispatcher.parse(command, source), command); ++ this.performCommand(this.dispatcher.parse(command, source), command, label); ++ // CraftBukkit end + } + + public void performCommand(ParseResults parseResults, String command) { ++ // CraftBukkit start ++ this.performCommand(parseResults, command, command); ++ } ++ ++ public void performCommand(ParseResults parseResults, String command, String label) { ++ // CraftBukkit end ++ // Paper start ++ this.performCommand(parseResults, command, label, false); ++ } ++ public void performCommand(ParseResults parseResults, String command, String label, boolean throwCommandError) { ++ // Paper end + CommandSourceStack commandSourceStack = parseResults.getContext().getSource(); + Profiler.get().push(() -> "/" + command); +- ContextChain contextChain = finishParsing(parseResults, command, commandSourceStack); ++ ContextChain contextChain = this.finishParsing(parseResults, command, commandSourceStack, label); // CraftBukkit // Paper - Add UnknownCommandEvent + + try { + if (contextChain != null) { +@@ -280,9 +_,10 @@ + ); + } + } catch (Exception var12) { ++ if (throwCommandError) throw var12; // Paper + MutableComponent mutableComponent = Component.literal(var12.getMessage() == null ? var12.getClass().getName() : var12.getMessage()); +- if (LOGGER.isDebugEnabled()) { +- LOGGER.error("Command exception: /{}", command, var12); ++ Commands.LOGGER.error("Command exception: /{}", command, var12); // Paper - always show execution exception in console log ++ if (commandSourceStack.getServer().isDebugging() || Commands.LOGGER.isDebugEnabled()) { // Paper - Debugging + StackTraceElement[] stackTrace = var12.getStackTrace(); + + for (int i = 0; i < Math.min(stackTrace.length, 3); i++) { +@@ -309,18 +_,22 @@ + } + + @Nullable +- private static ContextChain finishParsing(ParseResults parseResults, String command, CommandSourceStack source) { ++ private ContextChain finishParsing(ParseResults parseResults, String command, CommandSourceStack source, String label) { // CraftBukkit // Paper - Add UnknownCommandEvent + try { + validateParseResults(parseResults); + return ContextChain.tryFlatten(parseResults.getContext().build(command)) + .orElseThrow(() -> CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parseResults.getReader())); + } catch (CommandSyntaxException var7) { +- source.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage())); ++ // Paper start - Add UnknownCommandEvent ++ final net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text(); ++ // source.sendFailure(ComponentUtils.fromMessage(var7.getRawMessage())); ++ builder.color(net.kyori.adventure.text.format.NamedTextColor.RED).append(io.papermc.paper.brigadier.PaperBrigadier.componentFromMessage(var7.getRawMessage())); ++ // Paper end - Add UnknownCommandEvent + if (var7.getInput() != null && var7.getCursor() >= 0) { + int min = Math.min(var7.getInput().length(), var7.getCursor()); + MutableComponent mutableComponent = Component.empty() + .withStyle(ChatFormatting.GRAY) +- .withStyle(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command))); ++ .withStyle(style -> style.withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + label))); // CraftBukkit // Paper + if (min > 10) { + mutableComponent.append(CommonComponents.ELLIPSIS); + } +@@ -332,7 +_,17 @@ + } + + mutableComponent.append(Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC)); +- source.sendFailure(mutableComponent); ++ // Paper start - Add UnknownCommandEvent ++ // source.sendFailure(mutableComponent); ++ builder ++ .append(net.kyori.adventure.text.Component.newline()) ++ .append(io.papermc.paper.adventure.PaperAdventure.asAdventure(mutableComponent)); ++ } ++ org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(source.getBukkitSender(), command, org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty() ? null : builder.build()); ++ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(event); ++ if (event.message() != null) { ++ source.sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); ++ // Paper end - Add UnknownCommandEvent + } + + return null; +@@ -360,25 +_,130 @@ + } + + public void sendCommands(ServerPlayer player) { +- Map, CommandNode> map = Maps.newHashMap(); ++ // Paper start - Send empty commands if tab completion is disabled ++ if (org.spigotmc.SpigotConfig.tabComplete < 0) { ++ player.connection.send(new ClientboundCommandsPacket(new RootCommandNode<>())); ++ return; ++ } ++ // Paper end - Send empty commands if tab completion is disabled ++ // CraftBukkit start ++ // Register Vanilla commands into builtRoot as before ++ // Paper start - Perf: Async command map building ++ // Copy root children to avoid concurrent modification during building ++ final Collection> commandNodes = new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren()); ++ COMMAND_SENDING_POOL.execute(() -> this.sendAsync(player, commandNodes)); ++ } ++ ++ // Fixed pool, but with discard policy ++ public static final java.util.concurrent.ExecutorService COMMAND_SENDING_POOL = new java.util.concurrent.ThreadPoolExecutor( ++ 2, 2, 0, java.util.concurrent.TimeUnit.MILLISECONDS, ++ new java.util.concurrent.LinkedBlockingQueue<>(), ++ new com.google.common.util.concurrent.ThreadFactoryBuilder() ++ .setNameFormat("Paper Async Command Builder Thread Pool - %1$d") ++ .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)) ++ .build(), ++ new java.util.concurrent.ThreadPoolExecutor.DiscardPolicy() ++ ); ++ ++ private void sendAsync(ServerPlayer player, Collection> dispatcherRootChildren) { ++ // Paper end - Perf: Async command map building ++ Map, CommandNode> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues ++ // Paper - brigadier API removes the need to fill the map twice + RootCommandNode rootCommandNode = new RootCommandNode<>(); + map.put(this.dispatcher.getRoot(), rootCommandNode); +- this.fillUsableCommands(this.dispatcher.getRoot(), rootCommandNode, player.createCommandSourceStack(), map); ++ this.fillUsableCommands(dispatcherRootChildren, rootCommandNode, player.createCommandSourceStack(), map); // Paper - Perf: Async command map building; pass copy of children ++ ++ Collection bukkit = new LinkedHashSet<>(); ++ for (CommandNode node : rootCommandNode.getChildren()) { ++ bukkit.add(node.getName()); ++ } ++ // Paper start - Perf: Async command map building ++ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootCommandNode, false).callEvent(); // Paper - Brigadier API ++ net.minecraft.server.MinecraftServer.getServer().execute(() -> { ++ runSync(player, bukkit, rootCommandNode); ++ }); ++ } ++ ++ private void runSync(ServerPlayer player, Collection bukkit, RootCommandNode rootCommandNode) { ++ // Paper end - Perf: Async command map building ++ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootCommandNode, true).callEvent(); // Paper - Brigadier API ++ PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit)); ++ event.getPlayer().getServer().getPluginManager().callEvent(event); ++ ++ // Remove labels that were removed during the event ++ for (String orig : bukkit) { ++ if (!event.getCommands().contains(orig)) { ++ rootCommandNode.removeCommand(orig); ++ } ++ } ++ // CraftBukkit end ++ + player.connection.send(new ClientboundCommandsPacket(rootCommandNode)); + } + + private void fillUsableCommands( +- CommandNode rootCommandSource, ++ Collection> children, // Paper - Perf: Async command map building; pass copy of children + CommandNode rootSuggestion, + CommandSourceStack source, + Map, CommandNode> commandNodeToSuggestionNode + ) { +- for (CommandNode commandNode : rootCommandSource.getChildren()) { ++ commandNodeToSuggestionNode.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains(":")); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below ++ for (CommandNode commandNode : children) { // Paper - Perf: Async command map building; pass copy of children ++ // Paper start - Brigadier API ++ if (commandNode.clientNode != null) { ++ commandNode = commandNode.clientNode; ++ } ++ // Paper end - Brigadier API ++ if (!org.spigotmc.SpigotConfig.sendNamespaced && commandNode.getName().contains(":")) continue; // Spigot + if (commandNode.canUse(source)) { + ArgumentBuilder argumentBuilder = (ArgumentBuilder) commandNode.createBuilder(); ++ // 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 && commandNodeToSuggestionNode.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 redirect = argumentBuilder.getRedirect(); ++ // Diff copied from LiteralCommand#createBuilder ++ final com.mojang.brigadier.builder.LiteralArgumentBuilder builder = com.mojang.brigadier.builder.LiteralArgumentBuilder.literal(commandNode.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 child : redirect.getChildren()) { ++ builder.then(child); ++ } ++ ++ argumentBuilder = builder; ++ } ++ // Paper end + argumentBuilder.requires(suggestions -> true); + if (argumentBuilder.getCommand() != null) { +- argumentBuilder.executes(commandContext -> 0); ++ // Paper start - fix suggestions due to falsely equal nodes ++ // Always create a new instance ++ argumentBuilder.executes(new com.mojang.brigadier.Command<>() { ++ @Override ++ public int run(com.mojang.brigadier.context.CommandContext commandContext) { ++ return 0; ++ } ++ }); ++ // Paper end - fix suggestions due to falsely equal nodes + } + + if (argumentBuilder instanceof RequiredArgumentBuilder) { +@@ -396,7 +_,7 @@ + commandNodeToSuggestionNode.put(commandNode, commandNode1); + rootSuggestion.addChild(commandNode1); + if (!commandNode.getChildren().isEmpty()) { +- this.fillUsableCommands(commandNode, commandNode1, source, commandNodeToSuggestionNode); ++ this.fillUsableCommands(commandNode.getChildren(), commandNode1, source, commandNodeToSuggestionNode); // Paper - Perf: Async command map building; pass copy of children + } + } + } diff --git a/paper-server/patches/sources/net/minecraft/world/CompoundContainer.java.patch b/paper-server/patches/sources/net/minecraft/world/CompoundContainer.java.patch index ffcea4eeab..54adbbf786 100644 --- a/paper-server/patches/sources/net/minecraft/world/CompoundContainer.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/CompoundContainer.java.patch @@ -1,8 +1,8 @@ --- a/net/minecraft/world/CompoundContainer.java +++ b/net/minecraft/world/CompoundContainer.java @@ -7,6 +_,48 @@ - private final Container container1; - private final Container container2; + public final Container container1; + public final Container container2; + // Paper start - add fields and methods + public java.util.List transaction = new java.util.ArrayList(); diff --git a/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch similarity index 57% rename from paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch rename to paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch index ac1686a6c3..92e7c74467 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java.patch @@ -1,11 +1,11 @@ --- a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java +++ b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java -@@ -39,7 +39,7 @@ +@@ -39,7 +_,7 @@ @Override - public Path createPath(Entity entity, int distance) { -- return this.createPath(entity.blockPosition(), distance); -+ return this.createPath(entity.blockPosition(), entity, distance); // Paper - EntityPathfindEvent + public Path createPath(Entity entity, int i) { +- return this.createPath(entity.blockPosition(), i); ++ return this.createPath(entity.blockPosition(), entity, i); // Paper - EntityPathfindEvent } @Override diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch new file mode 100644 index 0000000000..1880055015 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch @@ -0,0 +1,46 @@ +--- a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java ++++ b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java +@@ -41,7 +_,7 @@ + } + + @Override +- public Path createPath(BlockPos pos, int accuracy) { ++ public Path createPath(BlockPos pos, @javax.annotation.Nullable Entity entity, int accuracy) { // Paper - EntityPathfindEvent + LevelChunk chunkNow = this.level.getChunkSource().getChunkNow(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); + if (chunkNow == null) { + return null; +@@ -54,7 +_,7 @@ + } + + if (mutableBlockPos.getY() > this.level.getMinY()) { +- return super.createPath(mutableBlockPos.above(), accuracy); ++ return super.createPath(mutableBlockPos.above(), entity, accuracy); // Paper - EntityPathfindEvent + } + + mutableBlockPos.setY(pos.getY() + 1); +@@ -67,7 +_,7 @@ + } + + if (!chunkNow.getBlockState(pos).isSolid()) { +- return super.createPath(pos, accuracy); ++ return super.createPath(pos, entity, accuracy); // Paper - EntityPathfindEvent + } else { + BlockPos.MutableBlockPos mutableBlockPos = pos.mutable().move(Direction.UP); + +@@ -75,14 +_,14 @@ + mutableBlockPos.move(Direction.UP); + } + +- return super.createPath(mutableBlockPos.immutable(), accuracy); ++ return super.createPath(mutableBlockPos.immutable(), entity, accuracy); // Paper - EntityPathfindEvent + } + } + } + + @Override + public Path createPath(Entity entity, int i) { +- return this.createPath(entity.blockPosition(), i); ++ return this.createPath(entity.blockPosition(), entity, i); // Paper - EntityPathfindEvent + } + + private int getSurfaceY() { diff --git a/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch similarity index 60% rename from paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch rename to paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch index 124ed48519..bd9b6ee1ee 100644 --- a/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/PathNavigation.java.patch @@ -1,64 +1,64 @@ --- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java +++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java -@@ -125,8 +125,14 @@ +@@ -125,7 +_,13 @@ @Nullable - public Path createPath(BlockPos target, int distance) { -- return this.createPath(ImmutableSet.of(target), 8, false, distance); + public Path createPath(BlockPos pos, int accuracy) { +- return this.createPath(ImmutableSet.of(pos), 8, false, accuracy); + // Paper start - EntityPathfindEvent -+ return this.createPath(target, null, distance); - } -+ @Nullable -+ public Path createPath(BlockPos target, @Nullable Entity entity, int distance) { -+ return this.createPath(ImmutableSet.of(target), entity, 8, false, distance); -+ // Paper end - EntityPathfindEvent ++ return this.createPath(pos, null, accuracy); + } - - @Nullable - public Path createPath(BlockPos target, int minDistance, int maxDistance) { -@@ -135,7 +141,7 @@ - - @Nullable - public Path createPath(Entity entity, int distance) { -- return this.createPath(ImmutableSet.of(entity.blockPosition()), 16, true, distance); -+ return this.createPath(ImmutableSet.of(entity.blockPosition()), entity, 16, true, distance); // Paper - EntityPathfindEvent ++ @Nullable ++ public Path createPath(BlockPos target, @Nullable Entity entity, int accuracy) { ++ return this.createPath(ImmutableSet.of(target), entity, 8, false, accuracy); ++ // Paper end - EntityPathfindEvent } @Nullable -@@ -145,6 +151,17 @@ +@@ -135,7 +_,7 @@ @Nullable - protected Path createPath(Set positions, int range, boolean useHeadPos, int distance, float followRange) { + public Path createPath(Entity entity, int accuracy) { +- return this.createPath(ImmutableSet.of(entity.blockPosition()), 16, true, accuracy); ++ return this.createPath(ImmutableSet.of(entity.blockPosition()), entity, 16, true, accuracy); // Paper - EntityPathfindEvent + } + + @Nullable +@@ -145,6 +_,18 @@ + + @Nullable + protected Path createPath(Set targets, int regionOffset, boolean offsetUpward, int accuracy, float followRange) { + // Paper start - EntityPathfindEvent -+ return this.createPath(positions, null, range, useHeadPos, distance, followRange); ++ return this.createPath(targets, null, regionOffset, offsetUpward, accuracy, followRange); + } + + @Nullable -+ protected Path createPath(Set positions, @Nullable Entity target, int range, boolean useHeadPos, int distance) { -+ return this.createPath(positions, target, range, useHeadPos, distance, (float) this.mob.getAttributeValue(Attributes.FOLLOW_RANGE)); ++ protected Path createPath(Set targets, @Nullable Entity target, int regionOffset, boolean offsetUpward, int accuracy) { ++ return this.createPath(targets, target, regionOffset, offsetUpward, accuracy, (float) this.mob.getAttributeValue(Attributes.FOLLOW_RANGE)); + } + -+ @Nullable protected Path createPath(Set positions, @Nullable Entity target, int range, boolean useHeadPos, int distance, float followRange) { ++ @Nullable ++ protected Path createPath(Set targets, @Nullable Entity target, int regionOffset, boolean offsetUpward, int accuracy, float followRange) { + // Paper end - EntityPathfindEvent - if (positions.isEmpty()) { + if (targets.isEmpty()) { return null; - } else if (this.mob.getY() < (double)this.level.getMinY()) { -@@ -154,6 +171,23 @@ - } else if (this.path != null && !this.path.isDone() && positions.contains(this.targetPos)) { + } else if (this.mob.getY() < this.level.getMinY()) { +@@ -154,6 +_,23 @@ + } else if (this.path != null && !this.path.isDone() && targets.contains(this.targetPos)) { return this.path; } else { + // Paper start - EntityPathfindEvent + boolean copiedSet = false; -+ for (BlockPos possibleTarget : positions) { ++ for (BlockPos possibleTarget : targets) { + if (!this.mob.getCommandSenderWorld().getWorldBorder().isWithinBounds(possibleTarget) || !new com.destroystokyo.paper.event.entity.EntityPathfindEvent(this.mob.getBukkitEntity(), // Paper - don't path out of world border + io.papermc.paper.util.MCUtil.toLocation(this.mob.level(), possibleTarget), target == null ? null : target.getBukkitEntity()).callEvent()) { + if (!copiedSet) { + copiedSet = true; -+ positions = new java.util.HashSet<>(positions); ++ targets = new java.util.HashSet<>(targets); + } + // note: since we copy the set this remove call is safe, since we're iterating over the old copy -+ positions.remove(possibleTarget); -+ if (positions.isEmpty()) { ++ targets.remove(possibleTarget); ++ if (targets.isEmpty()) { + return null; + } + } @@ -66,9 +66,9 @@ + // Paper end - EntityPathfindEvent ProfilerFiller profilerFiller = Profiler.get(); profilerFiller.push("pathfind"); - BlockPos blockPos = useHeadPos ? this.mob.blockPosition().above() : this.mob.blockPosition(); -@@ -175,13 +209,33 @@ - return this.moveTo(this.createPath(x, y, z, 1), speed); + BlockPos blockPos = offsetUpward ? this.mob.blockPosition().above() : this.mob.blockPosition(); +@@ -171,6 +_,11 @@ + } } + // Paper start - Perf: Optimise pathfinding @@ -76,8 +76,10 @@ + private int pathfindFailures = 0; + // Paper end - Perf: Optimise pathfinding + - public boolean moveTo(double x, double y, double z, int distance, double speed) { - return this.moveTo(this.createPath(x, y, z, distance), speed); + public boolean moveTo(double x, double y, double z, double speed) { + return this.moveTo(this.createPath(x, y, z, 1), speed); + } +@@ -180,8 +_,23 @@ } public boolean moveTo(Entity entity, double speed) { @@ -101,4 +103,4 @@ + // Paper end - Perf: Optimise pathfinding } - public boolean moveTo(@Nullable Path path, double speed) { + public boolean moveTo(@Nullable Path pathentity, double speed) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch new file mode 100644 index 0000000000..ca88f3f478 --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java ++++ b/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java +@@ -16,9 +_,9 @@ + } + + @Override +- public Path createPath(BlockPos pos, int accuracy) { ++ public Path createPath(BlockPos pos, @Nullable Entity entity, int accuracy) { + this.pathToPosition = pos; +- return super.createPath(pos, accuracy); ++ return super.createPath(pos, entity, accuracy); // Paper - EntityPathfindEvent + } + + @Override diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/horse/AbstractHorse.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/horse/AbstractHorse.java.patch index 180a6dcee4..1b2858a398 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/horse/AbstractHorse.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/horse/AbstractHorse.java.patch @@ -83,7 +83,7 @@ @@ -340,7 +_,7 @@ - protected void createInventory() { + public void createInventory() { SimpleContainer simpleContainer = this.inventory; - this.inventory = new SimpleContainer(this.getInventorySize()); + this.inventory = new SimpleContainer(this.getInventorySize(), (org.bukkit.entity.AbstractHorse) this.getBukkitEntity()); // CraftBukkit diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch index 11854ff324..24372abac2 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/BarrelBlockEntity.java.patch @@ -39,5 +39,5 @@ + } + // CraftBukkit end private NonNullList items = NonNullList.withSize(27, ItemStack.EMPTY); - private final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() { + public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() { @Override diff --git a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch index 746ad85ba8..6248b82627 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java.patch @@ -2,7 +2,7 @@ +++ b/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java @@ -27,6 +_,42 @@ private final NonNullList items = NonNullList.withSize(6, ItemStack.EMPTY); - private int lastInteractedSlot = -1; + public int lastInteractedSlot = -1; + // CraftBukkit start - add fields and methods + public java.util.List transaction = new java.util.ArrayList<>(); diff --git a/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch b/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch index c28a5c1792..e6ba6d525f 100644 --- a/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch @@ -2,10 +2,10 @@ +++ b/net/minecraft/world/level/dimension/end/EndDragonFight.java @@ -92,7 +_,7 @@ @Nullable - private BlockPos portalLocation; + public BlockPos portalLocation; @Nullable -- private DragonRespawnAnimation respawnStage; +- public DragonRespawnAnimation respawnStage; + public DragonRespawnAnimation respawnStage;// Paper-At: public net.minecraft.world.level.dimension.end.EndDragonFight respawnStage private int respawnTime; @Nullable - private List respawnCrystals; + public List respawnCrystals; diff --git a/paper-server/patches/unapplied/net/minecraft/commands/Commands.java.patch b/paper-server/patches/unapplied/net/minecraft/commands/Commands.java.patch deleted file mode 100644 index 1fc19d5217..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/commands/Commands.java.patch +++ /dev/null @@ -1,372 +0,0 @@ ---- a/net/minecraft/commands/Commands.java -+++ b/net/minecraft/commands/Commands.java -@@ -138,6 +138,14 @@ - import net.minecraft.world.flag.FeatureFlags; - import net.minecraft.world.level.GameRules; - import org.slf4j.Logger; -+ -+// CraftBukkit start -+import com.google.common.base.Joiner; -+import java.util.Collection; -+import java.util.LinkedHashSet; -+import org.bukkit.event.player.PlayerCommandSendEvent; -+import org.bukkit.event.server.ServerCommandEvent; -+// CraftBukkit end - - public class Commands { - -@@ -151,6 +159,7 @@ - private final com.mojang.brigadier.CommandDispatcher dispatcher = new com.mojang.brigadier.CommandDispatcher(); - - public Commands(Commands.CommandSelection environment, CommandBuildContext commandRegistryAccess) { -+ // Paper - AdvancementCommands.register(this.dispatcher); - AttributeCommand.register(this.dispatcher, commandRegistryAccess); - ExecuteCommand.register(this.dispatcher, commandRegistryAccess); -@@ -250,8 +259,33 @@ - - if (environment.includeIntegrated) { - PublishCommand.register(this.dispatcher); -+ } -+ -+ // Paper start - Vanilla command permission fixes -+ for (final CommandNode node : this.dispatcher.getRoot().getChildren()) { -+ if (node.getRequirement() == com.mojang.brigadier.builder.ArgumentBuilder.defaultRequirement()) { -+ node.requirement = stack -> stack.source == CommandSource.NULL || stack.getBukkitSender().hasPermission(org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(node)); -+ } - } -+ // Paper end - Vanilla command permission fixes -+ // Paper start - Brigadier Command API -+ // Create legacy minecraft namespace commands -+ for (final CommandNode node : new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren())) { -+ // The brigadier dispatcher is not able to resolve nested redirects. -+ // 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 flattenedAliasTarget = node; -+ while (flattenedAliasTarget.getRedirect() != null) flattenedAliasTarget = flattenedAliasTarget.getRedirect(); - -+ this.dispatcher.register( -+ com.mojang.brigadier.builder.LiteralArgumentBuilder.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()); - } - -@@ -262,30 +296,72 @@ - return new ParseResults(commandcontextbuilder1, parseResults.getReader(), parseResults.getExceptions()); - } - -+ // CraftBukkit start -+ public void dispatchServerCommand(CommandSourceStack sender, String command) { -+ Joiner joiner = Joiner.on(" "); -+ if (command.startsWith("/")) { -+ command = command.substring(1); -+ } -+ -+ ServerCommandEvent event = new ServerCommandEvent(sender.getBukkitSender(), command); -+ org.bukkit.Bukkit.getPluginManager().callEvent(event); -+ if (event.isCancelled()) { -+ return; -+ } -+ command = event.getCommand(); -+ -+ String[] args = command.split(" "); -+ if (args.length == 0) return; // Paper - empty commands shall not be dispatched -+ -+ // Paper - Fix permission levels for command blocks -+ -+ // Handle vanilla commands; // Paper - handled in CommandNode/CommandDispatcher -+ -+ String newCommand = joiner.join(args); -+ this.performPrefixedCommand(sender, newCommand, newCommand); -+ } -+ // CraftBukkit end -+ - public void performPrefixedCommand(CommandSourceStack source, String command) { -- command = command.startsWith("/") ? command.substring(1) : command; -- this.performCommand(this.dispatcher.parse(command, source), command); -+ // CraftBukkit start -+ this.performPrefixedCommand(source, command, command); -+ } -+ -+ public void performPrefixedCommand(CommandSourceStack commandlistenerwrapper, String s, String label) { -+ s = s.startsWith("/") ? s.substring(1) : s; -+ this.performCommand(this.dispatcher.parse(s, commandlistenerwrapper), s, label); -+ // CraftBukkit end - } - - public void performCommand(ParseResults parseResults, String command) { -- CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseResults.getContext().getSource(); -+ this.performCommand(parseResults, command, command); -+ } - -+ public void performCommand(ParseResults parseresults, String s, String label) { // CraftBukkit -+ // Paper start -+ this.performCommand(parseresults, s, label, false); -+ } -+ public void performCommand(ParseResults parseresults, String s, String label, boolean throwCommandError) { -+ // Paper end -+ CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource(); -+ - Profiler.get().push(() -> { -- return "/" + command; -+ return "/" + s; - }); -- ContextChain contextchain = Commands.finishParsing(parseResults, command, commandlistenerwrapper); -+ ContextChain contextchain = this.finishParsing(parseresults, s, commandlistenerwrapper, label); // CraftBukkit // Paper - Add UnknownCommandEvent - - try { - if (contextchain != null) { - Commands.executeCommandInContext(commandlistenerwrapper, (executioncontext) -> { -- ExecutionContext.queueInitialCommandExecution(executioncontext, command, contextchain, commandlistenerwrapper, CommandResultCallback.EMPTY); -+ ExecutionContext.queueInitialCommandExecution(executioncontext, s, contextchain, commandlistenerwrapper, CommandResultCallback.EMPTY); - }); - } - } catch (Exception exception) { -+ if (throwCommandError) throw exception; - MutableComponent ichatmutablecomponent = Component.literal(exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage()); - -- if (Commands.LOGGER.isDebugEnabled()) { -- 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 - StackTraceElement[] astacktraceelement = exception.getStackTrace(); - - for (int i = 0; i < Math.min(astacktraceelement.length, 3); ++i) { -@@ -298,7 +374,7 @@ - })); - if (SharedConstants.IS_RUNNING_IN_IDE) { - commandlistenerwrapper.sendFailure(Component.literal(Util.describeError(exception))); -- Commands.LOGGER.error("'/{}' threw an exception", command, exception); -+ Commands.LOGGER.error("'/{}' threw an exception", s, exception); - } - } finally { - Profiler.get().pop(); -@@ -307,18 +383,22 @@ - } - - @Nullable -- private static ContextChain finishParsing(ParseResults parseResults, String command, CommandSourceStack source) { -+ private ContextChain finishParsing(ParseResults parseresults, String s, CommandSourceStack commandlistenerwrapper, String label) { // CraftBukkit // Paper - Add UnknownCommandEvent - try { -- Commands.validateParseResults(parseResults); -- return (ContextChain) ContextChain.tryFlatten(parseResults.getContext().build(command)).orElseThrow(() -> { -- return CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parseResults.getReader()); -+ Commands.validateParseResults(parseresults); -+ return (ContextChain) ContextChain.tryFlatten(parseresults.getContext().build(s)).orElseThrow(() -> { -+ return CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand().createWithContext(parseresults.getReader()); - }); - } catch (CommandSyntaxException commandsyntaxexception) { -- source.sendFailure(ComponentUtils.fromMessage(commandsyntaxexception.getRawMessage())); -+ // Paper start - Add UnknownCommandEvent -+ final net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text(); -+ // commandlistenerwrapper.sendFailure(ComponentUtils.fromMessage(commandsyntaxexception.getRawMessage())); -+ builder.color(net.kyori.adventure.text.format.NamedTextColor.RED).append(io.papermc.paper.brigadier.PaperBrigadier.componentFromMessage(commandsyntaxexception.getRawMessage())); -+ // Paper end - Add UnknownCommandEvent - if (commandsyntaxexception.getInput() != null && commandsyntaxexception.getCursor() >= 0) { - int i = Math.min(commandsyntaxexception.getInput().length(), commandsyntaxexception.getCursor()); - MutableComponent ichatmutablecomponent = Component.empty().withStyle(ChatFormatting.GRAY).withStyle((chatmodifier) -> { -- return chatmodifier.withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + command)); -+ return chatmodifier.withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + label)); // CraftBukkit // Paper - }); - - if (i > 10) { -@@ -333,8 +413,18 @@ - } - - ichatmutablecomponent.append((Component) Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC)); -- source.sendFailure(ichatmutablecomponent); -+ // Paper start - Add UnknownCommandEvent -+ // commandlistenerwrapper.sendFailure(ichatmutablecomponent); -+ builder -+ .append(net.kyori.adventure.text.Component.newline()) -+ .append(io.papermc.paper.adventure.PaperAdventure.asAdventure(ichatmutablecomponent)); - } -+ org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(commandlistenerwrapper.getBukkitSender(), s, org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty() ? null : builder.build()); -+ org.bukkit.Bukkit.getServer().getPluginManager().callEvent(event); -+ if (event.message() != null) { -+ commandlistenerwrapper.sendFailure(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.message()), false); -+ // Paper end - Add UnknownCommandEvent -+ } - - return null; - } -@@ -368,7 +458,7 @@ - - executioncontext1.close(); - } finally { -- Commands.CURRENT_EXECUTION_CONTEXT.set((Object) null); -+ Commands.CURRENT_EXECUTION_CONTEXT.set(null); // CraftBukkit - decompile error - } - } else { - callback.accept(executioncontext); -@@ -377,30 +467,133 @@ - } - - public void sendCommands(ServerPlayer player) { -- Map, CommandNode> map = Maps.newHashMap(); -+ // Paper start - Send empty commands if tab completion is disabled -+ if (org.spigotmc.SpigotConfig.tabComplete < 0) { -+ player.connection.send(new ClientboundCommandsPacket(new RootCommandNode<>())); -+ return; -+ } -+ // Paper end - Send empty commands if tab completion is disabled -+ // CraftBukkit start -+ // Register Vanilla commands into builtRoot as before -+ // Paper start - Perf: Async command map building -+ // Copy root children to avoid concurrent modification during building -+ final Collection> commandNodes = new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren()); -+ COMMAND_SENDING_POOL.execute(() -> this.sendAsync(player, commandNodes)); -+ } -+ -+ // Fixed pool, but with discard policy -+ public static final java.util.concurrent.ExecutorService COMMAND_SENDING_POOL = new java.util.concurrent.ThreadPoolExecutor( -+ 2, 2, 0, java.util.concurrent.TimeUnit.MILLISECONDS, -+ new java.util.concurrent.LinkedBlockingQueue<>(), -+ new com.google.common.util.concurrent.ThreadFactoryBuilder() -+ .setNameFormat("Paper Async Command Builder Thread Pool - %1$d") -+ .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)) -+ .build(), -+ new java.util.concurrent.ThreadPoolExecutor.DiscardPolicy() -+ ); -+ -+ private void sendAsync(ServerPlayer player, Collection> dispatcherRootChildren) { -+ // Paper end - Perf: Async command map building -+ Map, CommandNode> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues -+ // Paper - brigadier API removes the need to fill the map twice - RootCommandNode rootcommandnode = new RootCommandNode(); - - map.put(this.dispatcher.getRoot(), rootcommandnode); -- this.fillUsableCommands(this.dispatcher.getRoot(), rootcommandnode, player.createCommandSourceStack(), map); -+ this.fillUsableCommands(dispatcherRootChildren, rootcommandnode, player.createCommandSourceStack(), map); // Paper - Perf: Async command map building; pass copy of children -+ -+ Collection bukkit = new LinkedHashSet<>(); -+ for (CommandNode node : rootcommandnode.getChildren()) { -+ bukkit.add(node.getName()); -+ } -+ // Paper start - Perf: Async command map building -+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper - Brigadier API -+ net.minecraft.server.MinecraftServer.getServer().execute(() -> { -+ runSync(player, bukkit, rootcommandnode); -+ }); -+ } -+ -+ private void runSync(ServerPlayer player, Collection bukkit, RootCommandNode rootcommandnode) { -+ // Paper end - Perf: Async command map building -+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, true).callEvent(); // Paper - Brigadier API -+ PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit)); -+ event.getPlayer().getServer().getPluginManager().callEvent(event); -+ -+ // Remove labels that were removed during the event -+ for (String orig : bukkit) { -+ if (!event.getCommands().contains(orig)) { -+ rootcommandnode.removeCommand(orig); -+ } -+ } -+ // CraftBukkit end - player.connection.send(new ClientboundCommandsPacket(rootcommandnode)); - } - -- private void fillUsableCommands(CommandNode tree, CommandNode result, CommandSourceStack source, Map, CommandNode> resultNodes) { -- Iterator iterator = tree.getChildren().iterator(); -+ // Paper start - Perf: Async command map building; pass copy of children -+ private void fillUsableCommands(Collection> children, CommandNode result, CommandSourceStack source, Map, CommandNode> 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(); -+ // Paper end - Perf: Async command map building - - while (iterator.hasNext()) { - CommandNode commandnode2 = (CommandNode) iterator.next(); -+ // Paper start - Brigadier API -+ if (commandnode2.clientNode != null) { -+ commandnode2 = commandnode2.clientNode; -+ } -+ // Paper end - Brigadier API -+ if ( !org.spigotmc.SpigotConfig.sendNamespaced && commandnode2.getName().contains( ":" ) ) continue; // Spigot - - if (commandnode2.canUse(source)) { -- ArgumentBuilder argumentbuilder = commandnode2.createBuilder(); -+ 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 redirect = argumentbuilder.getRedirect(); -+ // Diff copied from LiteralCommand#createBuilder -+ final com.mojang.brigadier.builder.LiteralArgumentBuilder 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 child : redirect.getChildren()) { -+ builder.then(child); -+ } -+ -+ argumentbuilder = builder; -+ } -+ // Paper end -+ - argumentbuilder.requires((icompletionprovider) -> { - return true; - }); - if (argumentbuilder.getCommand() != null) { -- argumentbuilder.executes((commandcontext) -> { -- return 0; -+ // Paper start - fix suggestions due to falsely equal nodes -+ argumentbuilder.executes(new com.mojang.brigadier.Command() { -+ @Override -+ public int run(com.mojang.brigadier.context.CommandContext commandContext) throws CommandSyntaxException { -+ return 0; -+ } - }); -+ // Paper end - } - - if (argumentbuilder instanceof RequiredArgumentBuilder) { -@@ -415,12 +608,12 @@ - argumentbuilder.redirect((CommandNode) resultNodes.get(argumentbuilder.getRedirect())); - } - -- CommandNode commandnode3 = argumentbuilder.build(); -+ CommandNode commandnode3 = argumentbuilder.build(); // CraftBukkit - decompile error - - resultNodes.put(commandnode2, commandnode3); - result.addChild(commandnode3); - if (!commandnode2.getChildren().isEmpty()) { -- this.fillUsableCommands(commandnode2, commandnode3, source, resultNodes); -+ this.fillUsableCommands(commandnode2.getChildren(), commandnode3, source, resultNodes); // Paper - Perf: Async command map building; pass children directly - } - } - } -@@ -481,7 +674,7 @@ - } - - private HolderLookup.RegistryLookup.Delegate createLookup(final HolderLookup.RegistryLookup original) { -- return new HolderLookup.RegistryLookup.Delegate(this) { -+ return new HolderLookup.RegistryLookup.Delegate() { // CraftBukkit - decompile error - @Override - public HolderLookup.RegistryLookup parent() { - return original; diff --git a/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch b/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch deleted file mode 100644 index 36cc381dc3..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java.patch +++ /dev/null @@ -1,46 +0,0 @@ ---- a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java -+++ b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java -@@ -41,7 +41,7 @@ - } - - @Override -- public Path createPath(BlockPos target, int distance) { -+ public Path createPath(BlockPos target, @javax.annotation.Nullable Entity entity, int distance) { // Paper - EntityPathfindEvent - LevelChunk levelChunk = this.level - .getChunkSource() - .getChunkNow(SectionPos.blockToSectionCoord(target.getX()), SectionPos.blockToSectionCoord(target.getZ())); -@@ -56,7 +56,7 @@ - } - - if (mutableBlockPos.getY() > this.level.getMinY()) { -- return super.createPath(mutableBlockPos.above(), distance); -+ return super.createPath(mutableBlockPos.above(), entity, distance); // Paper - EntityPathfindEvent - } - - mutableBlockPos.setY(target.getY() + 1); -@@ -69,7 +69,7 @@ - } - - if (!levelChunk.getBlockState(target).isSolid()) { -- return super.createPath(target, distance); -+ return super.createPath(target, entity, distance); // Paper - EntityPathfindEvent - } else { - BlockPos.MutableBlockPos mutableBlockPos2 = target.mutable().move(Direction.UP); - -@@ -77,14 +77,14 @@ - mutableBlockPos2.move(Direction.UP); - } - -- return super.createPath(mutableBlockPos2.immutable(), distance); -+ return super.createPath(mutableBlockPos2.immutable(), entity, distance); // Paper - EntityPathfindEvent - } - } - } - - @Override - public Path createPath(Entity entity, int distance) { -- return this.createPath(entity.blockPosition(), distance); -+ return this.createPath(entity.blockPosition(), entity, distance); // Paper - EntityPathfindEvent - } - - private int getSurfaceY() { diff --git a/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch b/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch deleted file mode 100644 index b2662d7401..0000000000 --- a/paper-server/patches/unapplied/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java -+++ b/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java -@@ -16,9 +16,9 @@ - } - - @Override -- public Path createPath(BlockPos target, int distance) { -+ public Path createPath(BlockPos target, @Nullable Entity entity, int distance) { // Paper - EntityPathfindEvent - this.pathToPosition = target; -- return super.createPath(target, distance); -+ return super.createPath(target, entity, distance); // Paper - EntityPathfindEvent - } - - @Override