diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index 09212e672b..92c53790ec 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -48,7 +48,7 @@ import net.minecraft.world.level.GameRules; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; -@@ -192,12 +196,72 @@ +@@ -192,11 +196,71 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; @@ -58,7 +58,7 @@ import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import org.slf4j.Logger; - ++ +// CraftBukkit start +import io.papermc.paper.adventure.ChatProcessor; // Paper +import io.papermc.paper.adventure.PaperAdventure; // Paper @@ -117,10 +117,9 @@ +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.SmithingInventory; +// CraftBukkit end -+ + public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl implements ServerGamePacketListener, ServerPlayerConnection, TickablePacketListener { - static final Logger LOGGER = LogUtils.getLogger(); @@ -247,7 +311,7 @@ private boolean waitingForSwitchToConfig; @@ -130,14 +129,14 @@ this.chunkSender = new PlayerChunkSender(connection.isMemoryConnection()); this.player = player; player.connection = this; -@@ -256,9 +320,25 @@ +@@ -256,8 +320,24 @@ Objects.requireNonNull(server); this.signedMessageDecoder = SignedMessageChain.Decoder.unsigned(uuid, server::enforceSecureProfile); - this.chatMessageChain = new FutureChain(server); + this.chatMessageChain = new FutureChain(server.chatExecutor); // CraftBukkit - async chat } - ++ + // CraftBukkit start - add fields and methods + private int lastTick = MinecraftServer.currentTick; + private int allowedPlayerTicks = 1; @@ -153,10 +152,9 @@ + private float lastYaw = Float.MAX_VALUE; + private boolean justTeleported = false; + // CraftBukkit end -+ + @Override public void tick() { - if (this.ackBlockChangesUpTo > -1) { @@ -277,7 +357,7 @@ if (this.clientIsFloating && !this.player.isSleeping() && !this.player.isPassenger() && !this.player.isDeadOrDying()) { if (++this.aboveGroundTickCount > this.getMaximumFlyingTicks(this.player)) { @@ -336,20 +334,63 @@ this.player.getRecipeBook().setBookSetting(packet.getBookType(), packet.isOpen(), packet.isFiltering()); } -@@ -548,6 +723,12 @@ +@@ -545,18 +720,62 @@ + + } + ++ // Paper start - AsyncTabCompleteEvent ++ private static final java.util.concurrent.ExecutorService TAB_COMPLETE_EXECUTOR = java.util.concurrent.Executors.newFixedThreadPool(4, ++ new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Tab Complete Thread - #%d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); ++ // Paper end - AsyncTabCompleteEvent @Override public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket packet) { - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); +- PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); ++ // PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // Paper - AsyncTabCompleteEvent; run this async + // CraftBukkit start + if (!this.chatSpamThrottler.isIncrementAndUnderThreshold(1, 500) && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { + this.disconnect(Component.translatable("disconnect.spam")); + return; + } + // CraftBukkit end ++ // Paper start - AsyncTabCompleteEvent ++ TAB_COMPLETE_EXECUTOR.execute(() -> this.handleCustomCommandSuggestions0(packet)); ++ } ++ ++ private void handleCustomCommandSuggestions0(final ServerboundCommandSuggestionPacket packet) { StringReader stringreader = new StringReader(packet.getCommand()); if (stringreader.canRead() && stringreader.peek() == '/') { -@@ -557,6 +738,7 @@ + stringreader.skip(); ++ } ++ ++ final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getCraftPlayer(), packet.getCommand(), true, null); ++ event.callEvent(); ++ final List completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.completions(); ++ // If the event isn't handled, we can assume that we have no completions, and so we'll ask the server ++ if (!event.isHandled()) { ++ if (event.isCancelled()) { ++ return; ++ } ++ ++ // This needs to be on main ++ this.server.scheduleOnMain(() -> this.sendServerSuggestions(packet, stringreader)); ++ } else if (!completions.isEmpty()) { ++ final com.mojang.brigadier.suggestion.SuggestionsBuilder builder0 = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packet.getCommand(), stringreader.getTotalLength()); ++ final com.mojang.brigadier.suggestion.SuggestionsBuilder builder = builder0.createOffset(builder0.getInput().lastIndexOf(' ') + 1); ++ for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion completion : completions) { ++ final Integer intSuggestion = com.google.common.primitives.Ints.tryParse(completion.suggestion()); ++ if (intSuggestion != null) { ++ builder.suggest(intSuggestion, PaperAdventure.asVanilla(completion.tooltip())); ++ } else { ++ builder.suggest(completion.suggestion(), PaperAdventure.asVanilla(completion.tooltip())); ++ } ++ } ++ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join())); + } ++ } + ++ private void sendServerSuggestions(final ServerboundCommandSuggestionPacket packet, final StringReader stringreader) { ++ // Paper end - AsyncTabCompleteEvent ParseResults parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack()); this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> { @@ -357,7 +398,7 @@ Suggestions suggestions1 = suggestions.getList().size() <= 1000 ? suggestions : new Suggestions(suggestions.getRange(), suggestions.getList().subList(0, 1000)); this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions1)); -@@ -668,7 +850,7 @@ +@@ -668,7 +887,7 @@ ItemStack itemstack = iblockdata.getCloneItemStack(worldserver, blockposition, flag); if (!itemstack.isEmpty()) { @@ -366,7 +407,7 @@ ServerGamePacketListenerImpl.addBlockDataToItem(iblockdata, worldserver, blockposition, itemstack); } -@@ -866,6 +1048,13 @@ +@@ -866,6 +1085,13 @@ AbstractContainerMenu container = this.player.containerMenu; if (container instanceof MerchantMenu containermerchant) { @@ -380,7 +421,7 @@ if (!containermerchant.stillValid(this.player)) { ServerGamePacketListenerImpl.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, containermerchant); return; -@@ -879,6 +1068,13 @@ +@@ -879,6 +1105,13 @@ @Override public void handleEditBook(ServerboundEditBookPacket packet) { @@ -394,7 +435,7 @@ int i = packet.slot(); if (Inventory.isHotbarSlot(i) || i == 40) { -@@ -899,12 +1095,16 @@ +@@ -899,12 +1132,16 @@ } private void updateBookContents(List pages, int slotId) { @@ -412,7 +453,7 @@ } } -@@ -915,12 +1115,13 @@ +@@ -915,12 +1152,13 @@ ItemStack itemstack1 = itemstack.transmuteCopy(Items.WRITTEN_BOOK); itemstack1.remove(DataComponents.WRITABLE_BOOK_CONTENT); @@ -428,7 +469,7 @@ } } -@@ -982,7 +1183,7 @@ +@@ -982,7 +1220,7 @@ } else { ServerLevel worldserver = this.player.serverLevel(); @@ -437,7 +478,7 @@ if (this.tickCount == 0) { this.resetPosition(); } -@@ -997,7 +1198,15 @@ +@@ -997,7 +1235,15 @@ if (this.player.isPassenger()) { this.player.absMoveTo(this.player.getX(), this.player.getY(), this.player.getZ(), f, f1); this.player.serverLevel().getChunkSource().move(this.player); @@ -453,7 +494,7 @@ double d3 = this.player.getX(); double d4 = this.player.getY(); double d5 = this.player.getZ(); -@@ -1019,15 +1228,33 @@ +@@ -1019,15 +1265,33 @@ ++this.receivedMovePacketCount; int i = this.receivedMovePacketCount - this.knownMovePacketCount; @@ -489,7 +530,7 @@ ServerGamePacketListenerImpl.LOGGER.warn("{} moved too quickly! {},{},{}", new Object[]{this.player.getName().getString(), d6, d7, d8}); this.teleport(this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYRot(), this.player.getXRot()); return; -@@ -1043,12 +1270,40 @@ +@@ -1043,12 +1307,40 @@ boolean flag1 = d7 > 0.0D; if (this.player.onGround() && !packet.isOnGround() && flag1) { @@ -531,7 +572,7 @@ double d11 = d7; d6 = d0 - this.player.getX(); -@@ -1061,15 +1316,81 @@ +@@ -1061,15 +1353,81 @@ d10 = d6 * d6 + d7 * d7 + d8 * d8; boolean flag3 = false; @@ -615,7 +656,7 @@ this.player.absMoveTo(d0, d1, d2, f, f1); boolean flag4 = this.player.isAutoSpinAttack(); -@@ -1119,6 +1440,7 @@ +@@ -1119,6 +1477,7 @@ this.awaitingTeleportTime = this.tickCount; this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot()); } @@ -623,7 +664,7 @@ return true; } else { -@@ -1147,23 +1469,83 @@ +@@ -1147,23 +1506,83 @@ } public void teleport(double x, double y, double z, float yaw, float pitch) { @@ -710,7 +751,7 @@ if (this.player.hasClientLoaded()) { BlockPos blockposition = packet.getPos(); -@@ -1175,14 +1557,46 @@ +@@ -1175,14 +1594,46 @@ if (!this.player.isSpectator()) { ItemStack itemstack = this.player.getItemInHand(InteractionHand.OFF_HAND); @@ -759,7 +800,7 @@ this.player.drop(false); } -@@ -1218,9 +1632,31 @@ +@@ -1218,9 +1669,31 @@ } } @@ -791,7 +832,7 @@ if (this.player.hasClientLoaded()) { this.player.connection.ackBlockChangesUpTo(packet.getSequence()); ServerLevel worldserver = this.player.serverLevel(); -@@ -1244,6 +1680,7 @@ +@@ -1244,6 +1717,7 @@ if (blockposition.getY() <= i) { if (this.awaitingPositionFromClient == null && worldserver.mayInteract(this.player, blockposition)) { @@ -799,7 +840,7 @@ InteractionResult enuminteractionresult = this.player.gameMode.useItemOn(this.player, worldserver, itemstack, enumhand, movingobjectpositionblock); if (enuminteractionresult.consumesAction()) { -@@ -1281,6 +1718,8 @@ +@@ -1281,6 +1755,8 @@ @Override public void handleUseItem(ServerboundUseItemPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -808,7 +849,7 @@ if (this.player.hasClientLoaded()) { this.ackBlockChangesUpTo(packet.getSequence()); ServerLevel worldserver = this.player.serverLevel(); -@@ -1296,6 +1735,47 @@ +@@ -1296,6 +1772,47 @@ this.player.absRotateTo(f, f1); } @@ -856,7 +897,7 @@ InteractionResult enuminteractionresult = this.player.gameMode.useItem(this.player, worldserver, itemstack, enumhand); if (enuminteractionresult instanceof InteractionResult.Success) { -@@ -1321,7 +1801,7 @@ +@@ -1321,7 +1838,7 @@ Entity entity = packet.getEntity(worldserver); if (entity != null) { @@ -865,7 +906,7 @@ return; } } -@@ -1342,6 +1822,13 @@ +@@ -1342,6 +1859,13 @@ @Override public void onDisconnect(DisconnectionDetails info) { @@ -879,7 +920,7 @@ ServerGamePacketListenerImpl.LOGGER.info("{} lost connection: {}", this.player.getName().getString(), info.reason().getString()); this.removePlayerFromWorld(); super.onDisconnect(info); -@@ -1349,10 +1836,20 @@ +@@ -1349,10 +1873,20 @@ private void removePlayerFromWorld() { this.chatMessageChain.close(); @@ -902,7 +943,7 @@ this.player.getTextFilter().leave(); } -@@ -1367,7 +1864,16 @@ +@@ -1367,7 +1901,16 @@ @Override public void handleSetCarriedItem(ServerboundSetCarriedItemPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -919,7 +960,7 @@ if (this.player.getInventory().selected != packet.getSlot() && this.player.getUsedItemHand() == InteractionHand.MAIN_HAND) { this.player.stopUsingItem(); } -@@ -1376,11 +1882,18 @@ +@@ -1376,11 +1919,18 @@ this.player.resetLastActionTime(); } else { ServerGamePacketListenerImpl.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString()); @@ -938,7 +979,7 @@ Optional optional = this.unpackAndApplyLastSeen(packet.lastSeenMessages()); if (!optional.isEmpty()) { -@@ -1394,27 +1907,44 @@ +@@ -1394,27 +1944,44 @@ return; } @@ -990,7 +1031,7 @@ ParseResults parseresults = this.parseCommand(command); if (this.server.enforceSecureProfile() && SignableCommand.hasSignableArguments(parseresults)) { -@@ -1431,19 +1961,37 @@ +@@ -1431,19 +1998,37 @@ if (!optional.isEmpty()) { this.tryHandleChat(packet.command(), () -> { @@ -1032,7 +1073,7 @@ } catch (SignedMessageChain.DecodeException signedmessagechain_a) { this.handleMessageDecodeFailure(signedmessagechain_a); return; -@@ -1451,10 +1999,10 @@ +@@ -1451,10 +2036,10 @@ CommandSigningContext.SignedArguments commandsigningcontext_a = new CommandSigningContext.SignedArguments(map); @@ -1045,7 +1086,7 @@ } private void handleMessageDecodeFailure(SignedMessageChain.DecodeException exception) { -@@ -1530,14 +2078,20 @@ +@@ -1530,14 +2115,20 @@ return com_mojang_brigadier_commanddispatcher.parse(command, this.player.createCommandSourceStack()); } @@ -1070,10 +1111,12 @@ } } -@@ -1566,6 +2120,127 @@ - return false; - } +@@ -1564,8 +2155,129 @@ + } + return false; ++ } ++ + // CraftBukkit start - add method + public void chat(String s, PlayerChatMessage original, boolean async) { + if (s.isEmpty() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { @@ -1166,8 +1209,8 @@ + 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 @@ -1198,7 +1241,7 @@ private PlayerChatMessage getSignedMessage(ServerboundChatPacket packet, LastSeenMessages lastSeenMessages) throws SignedMessageChain.DecodeException { SignedMessageBody signedmessagebody = new SignedMessageBody(packet.message(), packet.timeStamp(), packet.salt(), lastSeenMessages); -@@ -1573,13 +2248,42 @@ +@@ -1573,13 +2285,42 @@ } private void broadcastChatMessage(PlayerChatMessage message) { @@ -1246,7 +1289,7 @@ this.disconnect((Component) Component.translatable("disconnect.spam")); } -@@ -1601,7 +2305,33 @@ +@@ -1601,7 +2342,33 @@ @Override public void handleAnimate(ServerboundSwingPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -1280,7 +1323,7 @@ this.player.swing(packet.getHand()); } -@@ -1609,6 +2339,29 @@ +@@ -1609,6 +2376,29 @@ public void handlePlayerCommand(ServerboundPlayerCommandPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); if (this.player.hasClientLoaded()) { @@ -1310,7 +1353,7 @@ this.player.resetLastActionTime(); Entity entity; PlayerRideableJumping ijumpable; -@@ -1616,6 +2369,11 @@ +@@ -1616,6 +2406,11 @@ switch (packet.getAction()) { case PRESS_SHIFT_KEY: this.player.setShiftKeyDown(true); @@ -1322,7 +1365,7 @@ break; case RELEASE_SHIFT_KEY: this.player.setShiftKeyDown(false); -@@ -1691,6 +2449,12 @@ +@@ -1691,6 +2486,12 @@ } public void sendPlayerChatMessage(PlayerChatMessage message, ChatType.Bound params) { @@ -1335,7 +1378,7 @@ 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); } -@@ -1703,6 +2467,13 @@ +@@ -1703,6 +2504,13 @@ return this.connection.getRemoteAddress(); } @@ -1349,7 +1392,7 @@ public void switchToConfig() { this.waitingForSwitchToConfig = true; this.removePlayerFromWorld(); -@@ -1718,9 +2489,17 @@ +@@ -1718,9 +2526,17 @@ @Override public void handleInteract(ServerboundInteractPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -1367,7 +1410,7 @@ this.player.resetLastActionTime(); this.player.setShiftKeyDown(packet.isUsingSecondaryAction()); -@@ -1733,20 +2512,58 @@ +@@ -1733,20 +2549,58 @@ if (this.player.canInteractWithEntity(axisalignedbb, 3.0D)) { packet.dispatch(new ServerboundInteractPacket.Handler() { @@ -1383,7 +1426,7 @@ + ItemStack itemInHand = ServerGamePacketListenerImpl.this.player.getItemInHand(enumhand); + boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEAD && entity instanceof Mob; + Item origItem = ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null ? null : ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem(); -+ + + ServerGamePacketListenerImpl.this.cserver.getPluginManager().callEvent(event); + + // Entity in bucket - SPIGOT-4048 and SPIGOT-6859a @@ -1412,7 +1455,7 @@ + } + // CraftBukkit end + InteractionResult enuminteractionresult = playerconnection_a.run(ServerGamePacketListenerImpl.this.player, entity, enumhand); - ++ + // CraftBukkit start + if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) { + ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote(); @@ -1430,7 +1473,7 @@ } } -@@ -1755,19 +2572,20 @@ +@@ -1755,19 +2609,20 @@ @Override public void onInteraction(InteractionHand hand) { @@ -1454,7 +1497,7 @@ label23: { if (entity instanceof AbstractArrow) { -@@ -1785,6 +2603,11 @@ +@@ -1785,6 +2640,11 @@ } ServerGamePacketListenerImpl.this.player.attack(entity); @@ -1466,7 +1509,7 @@ return; } } -@@ -1795,7 +2618,26 @@ +@@ -1795,7 +2655,26 @@ }); } } @@ -1493,7 +1536,7 @@ } } -@@ -1809,7 +2651,7 @@ +@@ -1809,7 +2688,7 @@ case PERFORM_RESPAWN: if (this.player.wonGame) { this.player.wonGame = false; @@ -1502,7 +1545,7 @@ this.resetPosition(); CriteriaTriggers.CHANGED_DIMENSION.trigger(this.player, Level.END, Level.OVERWORLD); } else { -@@ -1817,11 +2659,11 @@ +@@ -1817,11 +2696,11 @@ return; } @@ -1516,7 +1559,7 @@ } } break; -@@ -1834,15 +2676,21 @@ +@@ -1834,15 +2713,21 @@ @Override public void handleContainerClose(ServerboundContainerClosePacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -1540,7 +1583,7 @@ this.player.containerMenu.sendAllDataToRemote(); } else if (!this.player.containerMenu.stillValid(this.player)) { ServerGamePacketListenerImpl.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu); -@@ -1855,7 +2703,284 @@ +@@ -1855,7 +2740,284 @@ boolean flag = packet.getStateId() != this.player.containerMenu.getStateId(); this.player.containerMenu.suppressRemoteUpdates(); @@ -1826,7 +1869,7 @@ ObjectIterator objectiterator = Int2ObjectMaps.fastIterable(packet.getChangedSlots()).iterator(); while (objectiterator.hasNext()) { -@@ -1901,8 +3026,22 @@ +@@ -1901,8 +3063,22 @@ return; } @@ -1850,7 +1893,7 @@ if (containerrecipebook_a == RecipeBookMenu.PostPlaceAction.PLACE_GHOST_RECIPE) { this.player.connection.send(new ClientboundPlaceGhostRecipePacket(this.player.containerMenu.containerId, craftingmanager_d.display().display())); } -@@ -1917,6 +3056,7 @@ +@@ -1917,6 +3093,7 @@ @Override public void handleContainerButtonClick(ServerboundContainerButtonClickPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -1858,7 +1901,7 @@ this.player.resetLastActionTime(); if (this.player.containerMenu.containerId == packet.containerId() && !this.player.isSpectator()) { if (!this.player.containerMenu.stillValid(this.player)) { -@@ -1945,7 +3085,44 @@ +@@ -1945,7 +3122,44 @@ boolean flag1 = packet.slotNum() >= 1 && packet.slotNum() <= 45; boolean flag2 = itemstack.isEmpty() || itemstack.getCount() <= itemstack.getMaxStackSize(); @@ -1866,7 +1909,7 @@ + // CraftBukkit start - Call click event + InventoryView inventory = this.player.inventoryMenu.getBukkitView(); + org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packet.itemStack()); - ++ + SlotType type = SlotType.QUICKBAR; + if (flag) { + type = SlotType.OUTSIDE; @@ -1881,7 +1924,7 @@ + this.cserver.getPluginManager().callEvent(event); + + itemstack = CraftItemStack.asNMSCopy(event.getCursor()); -+ + + switch (event.getResult()) { + case ALLOW: + // Plugin cleared the id / stacksize checks @@ -1903,7 +1946,7 @@ if (flag1 && flag2) { this.player.inventoryMenu.getSlot(packet.slotNum()).setByPlayer(itemstack); this.player.inventoryMenu.setRemoteSlot(packet.slotNum(), itemstack); -@@ -1972,6 +3149,7 @@ +@@ -1972,6 +3186,7 @@ } private void updateSignText(ServerboundSignUpdatePacket packet, List signText) { @@ -1911,7 +1954,7 @@ this.player.resetLastActionTime(); ServerLevel worldserver = this.player.serverLevel(); BlockPos blockposition = packet.getPos(); -@@ -1993,7 +3171,17 @@ +@@ -1993,7 +3208,17 @@ @Override public void handlePlayerAbilities(ServerboundPlayerAbilitiesPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -1930,7 +1973,7 @@ } @Override -@@ -2002,6 +3190,7 @@ +@@ -2002,6 +3227,7 @@ boolean flag = this.player.isModelPartShown(PlayerModelPart.HAT); this.player.updateOptions(packet.information()); @@ -1938,7 +1981,7 @@ if (this.player.isModelPartShown(PlayerModelPart.HAT) != flag) { this.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT, this.player)); } -@@ -2058,7 +3247,7 @@ +@@ -2058,7 +3284,7 @@ if (!this.waitingForSwitchToConfig) { throw new IllegalStateException("Client acknowledged config, but none was requested"); } else { @@ -1947,7 +1990,7 @@ } } -@@ -2083,8 +3272,10 @@ +@@ -2083,8 +3309,10 @@ }); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java index a2b62970a5..35ec2e57ed 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -2297,7 +2297,7 @@ public final class CraftServer implements Server { offers = this.tabCompleteChat(player, message); } - TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers); + TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers, message.startsWith("/") || forceCommand, pos != null ? io.papermc.paper.util.MCUtil.toLocation(((CraftWorld) player.getWorld()).getHandle(), BlockPos.containing(pos)) : null); // Paper - AsyncTabCompleteEvent this.getPluginManager().callEvent(tabEvent); return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions(); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/paper-server/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java index 0b27172073..15bc85f479 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java @@ -28,6 +28,61 @@ public class ConsoleCommandCompleter implements Completer { public void complete(LineReader reader, ParsedLine line, List candidates) { final CraftServer server = this.server.server; final String buffer = "/" + line.line(); + // Async Tab Complete + final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event = + new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(server.getConsoleSender(), buffer, true, null); + event.callEvent(); + final List completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.completions(); + + if (event.isCancelled() || event.isHandled()) { + // Still fire sync event with the provided completions, if someone is listening + if (!event.isCancelled() && TabCompleteEvent.getHandlerList().getRegisteredListeners().length > 0) { + List finalCompletions = new java.util.ArrayList<>(completions); + Waitable> syncCompletions = new Waitable>() { + @Override + protected List evaluate() { + org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(server.getConsoleSender(), buffer, + finalCompletions.stream() + .map(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion::suggestion) + .collect(java.util.stream.Collectors.toList())); + return syncEvent.callEvent() ? syncEvent.getCompletions() : com.google.common.collect.ImmutableList.of(); + } + }; + server.getServer().processQueue.add(syncCompletions); + try { + final List legacyCompletions = syncCompletions.get(); + completions.removeIf(it -> !legacyCompletions.contains(it.suggestion())); // remove any suggestions that were removed + // add any new suggestions + for (final String completion : legacyCompletions) { + if (notNewSuggestion(completions, completion)) { + continue; + } + completions.add(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion.completion(completion)); + } + } catch (InterruptedException | ExecutionException e1) { + e1.printStackTrace(); + } + } + + if (!completions.isEmpty()) { + for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion completion : completions) { + if (completion.suggestion().isEmpty()) { + continue; + } + candidates.add(new Candidate( + completion.suggestion(), + completion.suggestion(), + null, + io.papermc.paper.adventure.PaperAdventure.PLAIN.serializeOr(completion.tooltip(), null), + null, + null, + false + )); + } + } + return; + } + // Paper end Waitable> waitable = new Waitable>() { @Override @@ -73,4 +128,15 @@ public class ConsoleCommandCompleter implements Completer { Thread.currentThread().interrupt(); } } + + // Paper start + private boolean notNewSuggestion(final List completions, final String completion) { + for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion it : completions) { + if (it.suggestion().equals(completion)) { + return true; + } + } + return false; + } + // Paper end }