Paper/patch-remap/mache-spigotflower-stripped/net/minecraft/server/network/ServerGamePacketListenerImp...

1683 lines
98 KiB
Diff

--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -184,6 +185,62 @@
import net.minecraft.world.phys.shapes.VoxelShape;
import org.slf4j.Logger;
+// CraftBukkit start
+import com.mojang.datafixers.util.Pair;
+import java.util.Arrays;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import net.minecraft.network.chat.OutgoingChatMessage;
+import net.minecraft.world.entity.animal.Bucketable;
+import net.minecraft.world.entity.animal.allay.Allay;
+import net.minecraft.world.entity.item.ItemEntity;
+import net.minecraft.world.inventory.InventoryClickType;
+import net.minecraft.world.inventory.MerchantMenu;
+import net.minecraft.world.inventory.RecipeBookMenu;
+import net.minecraft.world.inventory.Slot;
+import net.minecraft.world.item.crafting.RecipeHolder;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.entity.CraftEntity;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.util.CraftChatMessage;
+import org.bukkit.craftbukkit.util.CraftMagicNumbers;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.bukkit.craftbukkit.util.LazyPlayerSet;
+import org.bukkit.craftbukkit.util.Waitable;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Event;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.event.inventory.CraftItemEvent;
+import org.bukkit.event.inventory.InventoryAction;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCreativeEvent;
+import org.bukkit.event.inventory.InventoryType.SlotType;
+import org.bukkit.event.inventory.SmithItemEvent;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+import org.bukkit.event.player.PlayerAnimationEvent;
+import org.bukkit.event.player.PlayerAnimationType;
+import org.bukkit.event.player.PlayerChatEvent;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.bukkit.event.player.PlayerInteractAtEntityEvent;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+import org.bukkit.event.player.PlayerItemHeldEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerRespawnEvent.RespawnReason;
+import org.bukkit.event.player.PlayerSwapHandItemsEvent;
+import org.bukkit.event.player.PlayerTeleportEvent;
+import org.bukkit.event.player.PlayerToggleFlightEvent;
+import org.bukkit.event.player.PlayerToggleSneakEvent;
+import org.bukkit.event.player.PlayerToggleSprintEvent;
+import org.bukkit.inventory.CraftingInventory;
+import org.bukkit.inventory.EquipmentSlot;
+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();
@@ -195,7 +252,9 @@
public final PlayerChunkSender chunkSender;
private int tickCount;
private int ackBlockChangesUpTo = -1;
- private int chatSpamTickCount;
+ // CraftBukkit start - multithreaded fields
+ private final AtomicInteger chatSpamTickCount = new AtomicInteger();
+ // CraftBukkit end
private int dropSpamTickCount;
private double firstGoodX;
private double firstGoodY;
@@ -229,20 +288,36 @@
private final FutureChain chatMessageChain;
private boolean waitingForSwitchToConfig;
- public ServerGamePacketListenerImpl(MinecraftServer minecraftserver, Connection connection, ServerPlayer serverplayer, CommonListenerCookie commonlistenercookie) {
- super(minecraftserver, connection, commonlistenercookie);
- this.chunkSender = new PlayerChunkSender(connection.isMemoryConnection());
- connection.setListener(this);
- this.player = serverplayer;
- serverplayer.connection = this;
- serverplayer.getTextFilter().join();
- UUID uuid = serverplayer.getUUID();
+ public ServerGamePacketListenerImpl(MinecraftServer minecraftserver, Connection networkmanager, ServerPlayer entityplayer, CommonListenerCookie commonlistenercookie) {
+ super(minecraftserver, networkmanager, commonlistenercookie, entityplayer); // CraftBukkit
+ this.chunkSender = new PlayerChunkSender(networkmanager.isMemoryConnection());
+ networkmanager.setListener(this);
+ this.player = entityplayer;
+ entityplayer.connection = this;
+ entityplayer.getTextFilter().join();
+ UUID uuid = entityplayer.getUUID();
Objects.requireNonNull(minecraftserver);
this.signedMessageDecoder = SignedMessageChain.Decoder.unsigned(uuid, minecraftserver::enforceSecureProfile);
- this.chatMessageChain = new FutureChain(minecraftserver);
+ this.chatMessageChain = new FutureChain(minecraftserver.chatExecutor); // CraftBukkit - async chat
}
+ // CraftBukkit start - add fields
+ private int lastTick = MinecraftServer.currentTick;
+ private int allowedPlayerTicks = 1;
+ private int lastDropTick = MinecraftServer.currentTick;
+ private int lastBookTick = MinecraftServer.currentTick;
+ private int dropCount = 0;
+
+ // Get position of last block hit for BlockDamageLevel.STOPPED
+ private double lastPosX = Double.MAX_VALUE;
+ private double lastPosY = Double.MAX_VALUE;
+ private double lastPosZ = Double.MAX_VALUE;
+ private float lastPitch = Float.MAX_VALUE;
+ private float lastYaw = Float.MAX_VALUE;
+ private boolean justTeleported = false;
+ // CraftBukkit end
+
@Override
@Override
public void tick() {
@@ -295,15 +369,21 @@
}
this.keepConnectionAlive();
+ // CraftBukkit start
+ for (int spam; (spam = this.chatSpamTickCount.get()) > 0 && !chatSpamTickCount.compareAndSet(spam, spam - 1); ) ;
+ /* Use thread-safe field access instead
if (this.chatSpamTickCount > 0) {
--this.chatSpamTickCount;
}
+ */
+ // CraftBukkit end
if (this.dropSpamTickCount > 0) {
--this.dropSpamTickCount;
}
if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && Util.getMillis() - this.player.getLastActionTime() > (long) this.server.getPlayerIdleTimeout() * 1000L * 60L) {
+ this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854
this.disconnect(Component.translatable("multiplayer.disconnect.idling"));
}
@@ -399,7 +474,34 @@
double d9 = entity.getDeltaMovement().lengthSqr();
double d10 = d6 * d6 + d7 * d7 + d8 * d8;
- if (d10 - d9 > 100.0D && !this.isSingleplayerOwner()) {
+
+ // CraftBukkit start - handle custom speeds and skipped ticks
+ this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
+ this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
+ this.lastTick = (int) (System.currentTimeMillis() / 50);
+
+ ++this.receivedMovePacketCount;
+ int i = this.receivedMovePacketCount - this.knownMovePacketCount;
+ if (i > Math.max(this.allowedPlayerTicks, 5)) {
+ ServerGamePacketListenerImpl.LOGGER.debug(this.player.getScoreboardName() + " is sending move packets too frequently (" + i + " packets since last tick)");
+ i = 1;
+ }
+
+ if (d10 > 0) {
+ allowedPlayerTicks -= 1;
+ } else {
+ allowedPlayerTicks = 20;
+ }
+ double speed;
+ if (player.getAbilities().flying) {
+ speed = player.getAbilities().flyingSpeed * 20f;
+ } else {
+ speed = player.getAbilities().walkingSpeed * 10f;
+ }
+ speed *= 2f; // TODO: Get the speed of the vehicle instead of the player
+
+ if (d10 - d9 > Math.max(100.0D, Math.pow((double) (10.0F * (float) i * speed), 2)) && !this.isSingleplayerOwner()) {
+ // CraftBukkit end
ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", new Object[]{entity.getName().getString(), this.player.getName().getString(), d6, d7, d8});
this.send(new ClientboundMoveVehiclePacket(entity));
return;
@@ -439,14 +541,72 @@
}
entity.absMoveTo(d3, d4, d5, f, f1);
- boolean flag3 = serverlevel.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
+ player.absMoveTo(d3, d4, d5, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
+ boolean flag3 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
if (flag && (flag2 || !flag3)) {
entity.absMoveTo(d0, d1, d2, f, f1);
+ player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
this.send(new ClientboundMoveVehiclePacket(entity));
return;
}
+ // CraftBukkit start - fire PlayerMoveEvent
+ Player player = this.getCraftPlayer();
+ Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location.
+ Location to = player.getLocation().clone(); // Start off the To location as the Players current location.
+
+ // If the packet contains movement information then we update the To location with the correct XYZ.
+ to.setX(packet.getX());
+ to.setY(packet.getY());
+ to.setZ(packet.getZ());
+
+
+ // If the packet contains look information then we update the To location with the correct Yaw & Pitch.
+ to.setYaw(packet.getYRot());
+ to.setPitch(packet.getXRot());
+
+ // Prevent 40 event-calls for less than a single pixel of movement >.>
+ double delta = Math.pow(this.lastPosX - to.getX(), 2) + Math.pow(this.lastPosY - to.getY(), 2) + Math.pow(this.lastPosZ - to.getZ(), 2);
+ float deltaAngle = Math.abs(this.lastYaw - to.getYaw()) + Math.abs(this.lastPitch - to.getPitch());
+
+ if ((delta > 1f / 256 || deltaAngle > 10f) && !this.player.isImmobile()) {
+ this.lastPosX = to.getX();
+ this.lastPosY = to.getY();
+ this.lastPosZ = to.getZ();
+ this.lastYaw = to.getYaw();
+ this.lastPitch = to.getPitch();
+
+ // Skip the first time we do this
+ if (from.getX() != Double.MAX_VALUE) {
+ Location oldTo = to.clone();
+ PlayerMoveEvent event = new PlayerMoveEvent(player, from, to);
+ this.cserver.getPluginManager().callEvent(event);
+
+ // If the event is cancelled we move the player back to their old location.
+ if (event.isCancelled()) {
+ teleport(from);
+ return;
+ }
+
+ // If a Plugin has changed the To destination then we teleport the Player
+ // there to avoid any 'Moved wrongly' or 'Moved too quickly' errors.
+ // We only do this if the Event was not cancelled.
+ if (!oldTo.equals(event.getTo()) && !event.isCancelled()) {
+ this.player.getBukkitEntity().teleport(event.getTo(), PlayerTeleportEvent.TeleportCause.PLUGIN);
+ return;
+ }
+
+ // Check to see if the Players Location has some how changed during the call of the event.
+ // This can happen due to a plugin teleporting the player instead of using .setTo()
+ if (!from.equals(this.getCraftPlayer().getLocation()) && this.justTeleported) {
+ this.justTeleported = false;
+ return;
+ }
+ }
+ }
+ // CraftBukkit end
+
this.player.serverLevel().getChunkSource().move(this.player);
this.player.checkMovementStatistics(this.player.getX() - d0, this.player.getY() - d1, this.player.getZ() - d2);
this.clientVehicleIsFloating = d11 >= -0.03125D && !flag1 && !this.server.isFlightAllowed() && !entity.isNoGravity() && this.noBlocksAround(entity);
@@ -481,6 +640,7 @@
}
this.awaitingPositionFromClient = null;
+ this.player.serverLevel().getChunkSource().move(this.player); // CraftBukkit
}
}
@@ -497,10 +656,10 @@
}
@Override
- @Override
- public void handleRecipeBookChangeSettingsPacket(ServerboundRecipeBookChangeSettingsPacket serverboundrecipebookchangesettingspacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundrecipebookchangesettingspacket, this, this.player.serverLevel());
- this.player.getRecipeBook().setBookSetting(serverboundrecipebookchangesettingspacket.getBookType(), serverboundrecipebookchangesettingspacket.isOpen(), serverboundrecipebookchangesettingspacket.isFiltering());
+ public void handleRecipeBookChangeSettingsPacket(ServerboundRecipeBookChangeSettingsPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ CraftEventFactory.callRecipeBookSettingsEvent(this.player, packet.getBookType(), packet.isOpen(), packet.isFiltering()); // CraftBukkit
+ this.player.getRecipeBook().setBookSetting(packet.getBookType(), packet.isOpen(), packet.isFiltering());
}
@Override
@@ -519,10 +677,15 @@
}
@Override
- @Override
- public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket serverboundcommandsuggestionpacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundcommandsuggestionpacket, this, this.player.serverLevel());
- StringReader stringreader = new StringReader(serverboundcommandsuggestionpacket.getCommand());
+ public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ // CraftBukkit start
+ if (chatSpamTickCount.addAndGet(1) > 500 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
+ this.disconnect(Component.translatable("disconnect.spam"));
+ return;
+ }
+ // CraftBukkit end
+ StringReader stringreader = new StringReader(packet.getCommand());
if (stringreader.canRead() && stringreader.peek() == '/') {
stringreader.skip();
@@ -531,7 +694,8 @@
ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
- this.send(new ClientboundCommandSuggestionsPacket(serverboundcommandsuggestionpacket.getId(), suggestions));
+ if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
+ this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
});
}
@@ -785,8 +940,15 @@
int i = serverboundselecttradepacket.getItem();
AbstractContainerMenu abstractcontainermenu = this.player.containerMenu;
- if (abstractcontainermenu instanceof MerchantMenu) {
- MerchantMenu merchantmenu = (MerchantMenu) abstractcontainermenu;
+ if (container instanceof MerchantMenu) {
+ MerchantMenu containermerchant = (MerchantMenu) container;
+ // CraftBukkit start
+ final org.bukkit.event.inventory.TradeSelectEvent tradeSelectEvent = CraftEventFactory.callTradeSelectEvent(this.player, i, containermerchant);
+ if (tradeSelectEvent.isCancelled()) {
+ this.player.getBukkitEntity().updateInventory();
+ return;
+ }
+ // CraftBukkit end
if (!merchantmenu.stillValid(this.player)) {
ServerGamePacketListenerImpl.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, merchantmenu);
@@ -800,9 +962,15 @@
}
@Override
- @Override
- public void handleEditBook(ServerboundEditBookPacket serverboundeditbookpacket) {
- int i = serverboundeditbookpacket.getSlot();
+ public void handleEditBook(ServerboundEditBookPacket packet) {
+ // CraftBukkit start
+ if (this.lastBookTick + 20 > MinecraftServer.currentTick) {
+ this.disconnect("Book edited too quickly!");
+ return;
+ }
+ this.lastBookTick = MinecraftServer.currentTick;
+ // CraftBukkit end
+ int i = packet.getSlot();
if (Inventory.isHotbarSlot(i) || i == 40) {
List<String> list = Lists.newArrayList();
@@ -828,7 +996,7 @@
ItemStack itemstack = this.player.getInventory().getItem(i);
if (itemstack.is(Items.WRITABLE_BOOK)) {
- this.updateBookPages(list, UnaryOperator.identity(), itemstack);
+ this.updateBookPages(pages, UnaryOperator.identity(), itemstack.copy(), index, itemstack); // CraftBukkit
}
}
@@ -853,13 +1021,13 @@
this.updateBookPages(list, (s) -> {
return Component.Serializer.toJson(Component.literal(s));
- }, itemstack1);
- this.player.getInventory().setItem(i, itemstack1);
+ }, itemstack1, index, itemstack); // CraftBukkit
+ this.player.getInventory().setItem(index, itemstack); // CraftBukkit - event factory updates the hand book
}
}
- private void updateBookPages(List<FilteredText> list, UnaryOperator<String> unaryoperator, ItemStack itemstack) {
- ListTag listtag = new ListTag();
+ private void updateBookPages(List<FilteredText> list, UnaryOperator<String> unaryoperator, ItemStack itemstack, int slot, ItemStack handItem) { // CraftBukkit
+ ListTag nbttaglist = new ListTag();
if (this.player.isTextFilteringEnabled()) {
Stream stream = list.stream().map((filteredtext) -> {
@@ -887,7 +1055,8 @@
}
}
- itemstack.addTagElement("pages", listtag);
+ itemstack.addTagElement("pages", nbttaglist);
+ CraftEventFactory.handleEditBookEvent(player, slot, handItem, itemstack); // CraftBukkit
}
@Override
@@ -948,7 +1113,7 @@
} else {
ServerLevel serverlevel = this.player.serverLevel();
- if (!this.player.wonGame) {
+ if (!this.player.wonGame && !this.player.isImmobile()) { // CraftBukkit
if (this.tickCount == 0) {
this.resetPosition();
}
@@ -958,7 +1123,7 @@
this.awaitingTeleportTime = this.tickCount;
this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
}
-
+ this.allowedPlayerTicks = 20; // CraftBukkit
} else {
this.awaitingTeleportTime = this.tickCount;
double d0 = clampHorizontal(serverboundmoveplayerpacket.getX(this.player.getX()));
@@ -970,7 +1135,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);
+ this.allowedPlayerTicks = 20; // CraftBukkit
} else {
+ // CraftBukkit - Make sure the move is valid but then reset it for plugins to modify
+ double prevX = player.getX();
+ double prevY = player.getY();
+ double prevZ = player.getZ();
+ float prevYaw = player.getYRot();
+ float prevPitch = player.getXRot();
+ // CraftBukkit end
double d3 = this.player.getX();
double d4 = this.player.getY();
double d5 = this.player.getZ();
@@ -990,7 +1163,12 @@
++this.receivedMovePacketCount;
int i = this.receivedMovePacketCount - this.knownMovePacketCount;
- if (i > 5) {
+ // CraftBukkit start - handle custom speeds and skipped ticks
+ this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
+ this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
+ this.lastTick = (int) (System.currentTimeMillis() / 50);
+
+ if (i > Math.max(this.allowedPlayerTicks, 5)) {
ServerGamePacketListenerImpl.LOGGER.debug("{} is sending move packets too frequently ({} packets since last tick)", this.player.getName().getString(), i);
i = 1;
}
@@ -998,7 +1188,8 @@
if (!this.player.isChangingDimension() && (!this.player.level().getGameRules().getBoolean(GameRules.RULE_DISABLE_ELYTRA_MOVEMENT_CHECK) || !this.player.isFallFlying())) {
float f2 = this.player.isFallFlying() ? 300.0F : 100.0F;
- if (d10 - d9 > (double) (f2 * (float) i) && !this.isSingleplayerOwner()) {
+ if (d10 - d9 > Math.max(f2, Math.pow((double) (10.0F * (float) i * speed), 2)) && !this.isSingleplayerOwner()) {
+ // CraftBukkit end
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;
@@ -1019,7 +1210,8 @@
boolean flag1 = this.player.verticalCollisionBelow;
- this.player.move(MoverType.PLAYER, new Vec3(d6, d7, d8));
+ this.player.move(EnumMoveType.PLAYER, new Vec3(d6, d7, d8));
+ this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
double d11 = d7;
d6 = d0 - this.player.getX();
@@ -1037,10 +1229,71 @@
ServerGamePacketListenerImpl.LOGGER.warn("{} moved wrongly!", this.player.getName().getString());
}
- if (!this.player.noPhysics && !this.player.isSleeping() && (flag2 && serverlevel.noCollision(this.player, aabb) || this.isPlayerCollidingWithAnythingNew(serverlevel, aabb, d0, d1, d2))) {
- this.teleport(d3, d4, d5, f, f1);
- this.player.doCheckFallDamage(this.player.getX() - d3, this.player.getY() - d4, this.player.getZ() - d5, serverboundmoveplayerpacket.isOnGround());
+ if (!this.player.noPhysics && !this.player.isSleeping() && (flag2 && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew(worldserver, axisalignedbb, d0, d1, d2))) {
+ this.internalTeleport(d3, d4, d5, f, f1, Collections.emptySet()); // CraftBukkit - SPIGOT-1807: Don't call teleport event, when the client thinks the player is falling, because the chunks are not loaded on the client yet.
+ this.player.doCheckFallDamage(this.player.getX() - d3, this.player.getY() - d4, this.player.getZ() - d5, packet.isOnGround());
} else {
+ // CraftBukkit start - fire PlayerMoveEvent
+ // Reset to old location first
+ this.player.absMoveTo(prevX, prevY, prevZ, prevYaw, prevPitch);
+
+ Player player = this.getCraftPlayer();
+ Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location.
+ Location to = player.getLocation().clone(); // Start off the To location as the Players current location.
+
+ // If the packet contains movement information then we update the To location with the correct XYZ.
+ if (packet.hasPos) {
+ to.setX(packet.x);
+ to.setY(packet.y);
+ to.setZ(packet.z);
+ }
+
+ // If the packet contains look information then we update the To location with the correct Yaw & Pitch.
+ if (packet.hasRot) {
+ to.setYaw(packet.yRot);
+ to.setPitch(packet.xRot);
+ }
+
+ // Prevent 40 event-calls for less than a single pixel of movement >.>
+ double delta = Math.pow(this.lastPosX - to.getX(), 2) + Math.pow(this.lastPosY - to.getY(), 2) + Math.pow(this.lastPosZ - to.getZ(), 2);
+ float deltaAngle = Math.abs(this.lastYaw - to.getYaw()) + Math.abs(this.lastPitch - to.getPitch());
+
+ if ((delta > 1f / 256 || deltaAngle > 10f) && !this.player.isImmobile()) {
+ this.lastPosX = to.getX();
+ this.lastPosY = to.getY();
+ this.lastPosZ = to.getZ();
+ this.lastYaw = to.getYaw();
+ this.lastPitch = to.getPitch();
+
+ // Skip the first time we do this
+ if (from.getX() != Double.MAX_VALUE) {
+ Location oldTo = to.clone();
+ PlayerMoveEvent event = new PlayerMoveEvent(player, from, to);
+ this.cserver.getPluginManager().callEvent(event);
+
+ // If the event is cancelled we move the player back to their old location.
+ if (event.isCancelled()) {
+ teleport(from);
+ return;
+ }
+
+ // If a Plugin has changed the To destination then we teleport the Player
+ // there to avoid any 'Moved wrongly' or 'Moved too quickly' errors.
+ // We only do this if the Event was not cancelled.
+ if (!oldTo.equals(event.getTo()) && !event.isCancelled()) {
+ this.player.getBukkitEntity().teleport(event.getTo(), PlayerTeleportEvent.TeleportCause.PLUGIN);
+ return;
+ }
+
+ // Check to see if the Players Location has some how changed during the call of the event.
+ // This can happen due to a plugin teleporting the player instead of using .setTo()
+ if (!from.equals(this.getCraftPlayer().getLocation()) && this.justTeleported) {
+ this.justTeleported = false;
+ return;
+ }
+ }
+ }
+ // CraftBukkit end
this.player.absMoveTo(d0, d1, d2, f, f1);
this.clientIsFloating = d11 >= -0.03125D && !flag1 && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR && !this.server.isFlightAllowed() && !this.player.getAbilities().mayfly && !this.player.hasEffect(MobEffects.LEVITATION) && !this.player.isFallFlying() && !this.player.isAutoSpinAttack() && this.noBlocksAround(this.player);
this.player.serverLevel().getChunkSource().move(this.player);
@@ -1081,11 +1334,68 @@
return true;
}
- public void teleport(double d0, double d1, double d2, float f, float f1) {
- this.teleport(d0, d1, d2, f, f1, Collections.emptySet());
+ // CraftBukkit start - Delegate to teleport(Location)
+ public void teleport(double x, double d1, double y, float f, float z) {
+ this.teleport(x, d1, y, f, z, PlayerTeleportEvent.TeleportCause.UNKNOWN);
}
- public void teleport(double d0, double d1, double d2, float f, float f1, Set<RelativeMovement> set) {
+ public void teleport(double d0, double d1, double d2, float f, float f1, PlayerTeleportEvent.TeleportCause cause) {
+ this.teleport(d0, d1, d2, f, f1, Collections.emptySet(), cause);
+ }
+
+ public void teleport(double x, double d1, double y, float f, float z, Set<RelativeMovement> set) {
+ this.teleport(x, d1, y, f, z, set, PlayerTeleportEvent.TeleportCause.UNKNOWN);
+ }
+
+ public boolean teleport(double d0, double d1, double d2, float f, float f1, Set<RelativeMovement> set, PlayerTeleportEvent.TeleportCause cause) { // CraftBukkit - Return event status
+ Player player = this.getCraftPlayer();
+ Location from = player.getLocation();
+
+ double x = d0;
+ double y = d1;
+ double z = d2;
+ float yaw = f;
+ float pitch = f1;
+
+ Location to = new Location(this.getCraftPlayer().getWorld(), x, y, z, yaw, pitch);
+ // SPIGOT-5171: Triggered on join
+ if (from.equals(to)) {
+ this.internalTeleport(d0, d1, d2, f, f1, set);
+ return false; // CraftBukkit - Return event status
+ }
+
+ PlayerTeleportEvent event = new PlayerTeleportEvent(player, from.clone(), to.clone(), cause);
+ this.cserver.getPluginManager().callEvent(event);
+
+ if (event.isCancelled() || !to.equals(event.getTo())) {
+ set.clear(); // Can't relative teleport
+ to = event.isCancelled() ? event.getFrom() : event.getTo();
+ d0 = to.getX();
+ d1 = to.getY();
+ d2 = to.getZ();
+ f = to.getYaw();
+ f1 = to.getPitch();
+ }
+
+ this.internalTeleport(d0, d1, d2, f, f1, set);
+ return event.isCancelled(); // CraftBukkit - Return event status
+ }
+
+ public void teleport(Location dest) {
+ internalTeleport(dest.getX(), dest.getY(), dest.getZ(), dest.getYaw(), dest.getPitch(), Collections.emptySet());
+ }
+
+ private void internalTeleport(double d0, double d1, double d2, float f, float f1, Set<RelativeMovement> set) {
+ // CraftBukkit start
+ if (Float.isNaN(f)) {
+ f = 0;
+ }
+ if (Float.isNaN(f1)) {
+ f1 = 0;
+ }
+
+ this.justTeleported = true;
+ // CraftBukkit end
double d3 = set.contains(RelativeMovement.X) ? this.player.getX() : 0.0D;
double d4 = set.contains(RelativeMovement.Y) ? this.player.getY() : 0.0D;
double d5 = set.contains(RelativeMovement.Z) ? this.player.getZ() : 0.0D;
@@ -1097,16 +1407,24 @@
this.awaitingTeleport = 0;
}
+ // CraftBukkit start - update last location
+ this.lastPosX = this.awaitingPositionFromClient.x;
+ this.lastPosY = this.awaitingPositionFromClient.y;
+ this.lastPosZ = this.awaitingPositionFromClient.z;
+ this.lastYaw = f;
+ this.lastPitch = f1;
+ // CraftBukkit end
+
this.awaitingTeleportTime = this.tickCount;
this.player.absMoveTo(d0, d1, d2, f, f1);
this.player.connection.send(new ClientboundPlayerPositionPacket(d0 - d3, d1 - d4, d2 - d5, f - f2, f1 - f3, set, this.awaitingTeleport));
}
@Override
- @Override
- public void handlePlayerAction(ServerboundPlayerActionPacket serverboundplayeractionpacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundplayeractionpacket, this, this.player.serverLevel());
- BlockPos blockpos = serverboundplayeractionpacket.getPos();
+ public void handlePlayerAction(ServerboundPlayerActionPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ if (this.player.isImmobile()) return; // CraftBukkit
+ BlockPos blockposition = packet.getPos();
this.player.resetLastActionTime();
ServerboundPlayerActionPacket.Action serverboundplayeractionpacket_action = serverboundplayeractionpacket.getAction();
@@ -1116,14 +1434,46 @@
if (!this.player.isSpectator()) {
ItemStack itemstack = this.player.getItemInHand(InteractionHand.OFF_HAND);
- this.player.setItemInHand(InteractionHand.OFF_HAND, this.player.getItemInHand(InteractionHand.MAIN_HAND));
- this.player.setItemInHand(InteractionHand.MAIN_HAND, itemstack);
+ // CraftBukkit start - inspiration taken from DispenserRegistry (See SpigotCraft#394)
+ CraftItemStack mainHand = CraftItemStack.asCraftMirror(itemstack);
+ CraftItemStack offHand = CraftItemStack.asCraftMirror(this.player.getItemInHand(EnumHand.MAIN_HAND));
+ PlayerSwapHandItemsEvent swapItemsEvent = new PlayerSwapHandItemsEvent(getCraftPlayer(), mainHand.clone(), offHand.clone());
+ this.cserver.getPluginManager().callEvent(swapItemsEvent);
+ if (swapItemsEvent.isCancelled()) {
+ return;
+ }
+ if (swapItemsEvent.getOffHandItem().equals(offHand)) {
+ this.player.setItemInHand(EnumHand.OFF_HAND, this.player.getItemInHand(EnumHand.MAIN_HAND));
+ } else {
+ this.player.setItemInHand(EnumHand.OFF_HAND, CraftItemStack.asNMSCopy(swapItemsEvent.getOffHandItem()));
+ }
+ if (swapItemsEvent.getMainHandItem().equals(mainHand)) {
+ this.player.setItemInHand(EnumHand.MAIN_HAND, itemstack);
+ } else {
+ this.player.setItemInHand(EnumHand.MAIN_HAND, CraftItemStack.asNMSCopy(swapItemsEvent.getMainHandItem()));
+ }
+ // CraftBukkit end
this.player.stopUsingItem();
}
return;
case DROP_ITEM:
if (!this.player.isSpectator()) {
+ // limit how quickly items can be dropped
+ // If the ticks aren't the same then the count starts from 0 and we update the lastDropTick.
+ if (this.lastDropTick != MinecraftServer.currentTick) {
+ this.dropCount = 0;
+ this.lastDropTick = MinecraftServer.currentTick;
+ } else {
+ // Else we increment the drop count and check the amount.
+ this.dropCount++;
+ if (this.dropCount >= 20) {
+ LOGGER.warn(this.player.getScoreboardName() + " dropped their items too quickly!");
+ this.disconnect("You dropped your items too quickly (Hacking?)");
+ return;
+ }
+ }
+ // CraftBukkit end
this.player.drop(false);
}
@@ -1159,13 +1509,13 @@
}
@Override
- @Override
- public void handleUseItemOn(ServerboundUseItemOnPacket serverbounduseitemonpacket) {
- PacketUtils.ensureRunningOnSameThread(serverbounduseitemonpacket, this, this.player.serverLevel());
- this.player.connection.ackBlockChangesUpTo(serverbounduseitemonpacket.getSequence());
- ServerLevel serverlevel = this.player.serverLevel();
- InteractionHand interactionhand = serverbounduseitemonpacket.getHand();
- ItemStack itemstack = this.player.getItemInHand(interactionhand);
+ public void handleUseItemOn(ServerboundUseItemOnPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ if (this.player.isImmobile()) return; // CraftBukkit
+ this.player.connection.ackBlockChangesUpTo(packet.getSequence());
+ ServerLevel worldserver = this.player.serverLevel();
+ EnumHand enumhand = packet.getHand();
+ ItemStack itemstack = this.player.getItemInHand(enumhand);
if (itemstack.isItemEnabled(serverlevel.enabledFeatures())) {
BlockHitResult blockhitresult = serverbounduseitemonpacket.getHitResult();
@@ -1183,9 +1533,10 @@
this.player.resetLastActionTime();
int i = this.player.level().getMaxBuildHeight();
- if (blockpos.getY() < i) {
- if (this.awaitingPositionFromClient == null && this.player.distanceToSqr((double) blockpos.getX() + 0.5D, (double) blockpos.getY() + 0.5D, (double) blockpos.getZ() + 0.5D) < 64.0D && serverlevel.mayInteract(this.player, blockpos)) {
- InteractionResult interactionresult = this.player.gameMode.useItemOn(this.player, serverlevel, itemstack, interactionhand, blockhitresult);
+ if (blockposition.getY() < i) {
+ if (this.awaitingPositionFromClient == null && this.player.distanceToSqr((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D) < 64.0D && worldserver.mayInteract(this.player, blockposition)) {
+ this.player.stopUsingItem(); // CraftBukkit - SPIGOT-4706
+ InteractionResult enuminteractionresult = this.player.gameMode.useItemOn(this.player, worldserver, itemstack, enumhand, movingobjectpositionblock);
if (direction == Direction.UP && !interactionresult.consumesAction() && blockpos.getY() >= i - 1 && wasBlockPlacementAttempt(this.player, itemstack)) {
MutableComponent mutablecomponent = Component.translatable("build.tooHigh", i - 1).withStyle(ChatFormatting.RED);
@@ -1211,22 +1562,44 @@
}
@Override
- @Override
- public void handleUseItem(ServerboundUseItemPacket serverbounduseitempacket) {
- PacketUtils.ensureRunningOnSameThread(serverbounduseitempacket, this, this.player.serverLevel());
- this.ackBlockChangesUpTo(serverbounduseitempacket.getSequence());
- ServerLevel serverlevel = this.player.serverLevel();
- InteractionHand interactionhand = serverbounduseitempacket.getHand();
- ItemStack itemstack = this.player.getItemInHand(interactionhand);
+ public void handleUseItem(ServerboundUseItemPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ if (this.player.isImmobile()) return; // CraftBukkit
+ this.ackBlockChangesUpTo(packet.getSequence());
+ ServerLevel worldserver = this.player.serverLevel();
+ EnumHand enumhand = packet.getHand();
+ ItemStack itemstack = this.player.getItemInHand(enumhand);
this.player.resetLastActionTime();
- if (!itemstack.isEmpty() && itemstack.isItemEnabled(serverlevel.enabledFeatures())) {
- InteractionResult interactionresult = this.player.gameMode.useItem(this.player, serverlevel, itemstack, interactionhand);
+ if (!itemstack.isEmpty() && itemstack.isItemEnabled(worldserver.enabledFeatures())) {
+ // CraftBukkit start
+ // Raytrace to look for 'rogue armswings'
+ float f1 = this.player.getXRot();
+ float f2 = this.player.getYRot();
+ double d0 = this.player.getX();
+ double d1 = this.player.getY() + (double) this.player.getEyeHeight();
+ double d2 = this.player.getZ();
+ Vec3 vec3d = new Vec3(d0, d1, d2);
if (interactionresult.shouldSwing()) {
this.player.swing(interactionhand, true);
}
+ if (cancelled) {
+ this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524
+ return;
+ }
+ itemstack = this.player.getItemInHand(enumhand); // Update in case it was changed in the event
+ if (itemstack.isEmpty()) {
+ return;
+ }
+ // CraftBukkit end
+ InteractionResult enuminteractionresult = this.player.gameMode.useItem(this.player, worldserver, itemstack, enumhand);
+
+ if (enuminteractionresult.shouldSwing()) {
+ this.player.swing(enumhand, true);
+ }
+
}
}
@@ -1242,7 +1635,7 @@
Entity entity = serverboundteleporttoentitypacket.getEntity(serverlevel);
if (entity != null) {
- this.player.teleportTo(serverlevel, entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot());
+ this.player.teleportTo(worldserver, entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot(), org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.SPECTATE); // CraftBukkit
return;
}
}
@@ -1265,19 +1657,31 @@
}
@Override
- @Override
- public void onDisconnect(Component component) {
- ServerGamePacketListenerImpl.LOGGER.info("{} lost connection: {}", this.player.getName().getString(), component.getString());
+ public void onDisconnect(Component reason) {
+ // CraftBukkit start - Rarely it would send a disconnect line twice
+ if (this.processedDisconnect) {
+ return;
+ } else {
+ this.processedDisconnect = true;
+ }
+ // CraftBukkit end
+ ServerGamePacketListenerImpl.LOGGER.info("{} lost connection: {}", this.player.getName().getString(), reason.getString());
this.removePlayerFromWorld();
super.onDisconnect(component);
}
private void removePlayerFromWorld() {
this.chatMessageChain.close();
+ // CraftBukkit start - Replace vanilla quit message handling with our own.
+ /*
this.server.invalidateStatus();
this.server.getPlayerList().broadcastSystemMessage(Component.translatable("multiplayer.player.left", this.player.getDisplayName()).withStyle(ChatFormatting.YELLOW), false);
this.player.disconnect();
- this.server.getPlayerList().remove(this.player);
+ String quitMessage = this.server.getPlayerList().remove(this.player);
+ if ((quitMessage != null) && (quitMessage.length() > 0)) {
+ this.server.getPlayerList().broadcastMessage(CraftChatMessage.fromString(quitMessage));
+ }
+ // CraftBukkit end
this.player.getTextFilter().leave();
}
@@ -1290,11 +1696,19 @@
}
@Override
- @Override
- public void handleSetCarriedItem(ServerboundSetCarriedItemPacket serverboundsetcarrieditempacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundsetcarrieditempacket, this, this.player.serverLevel());
- if (serverboundsetcarrieditempacket.getSlot() >= 0 && serverboundsetcarrieditempacket.getSlot() < Inventory.getSelectionSize()) {
- if (this.player.getInventory().selected != serverboundsetcarrieditempacket.getSlot() && this.player.getUsedItemHand() == InteractionHand.MAIN_HAND) {
+ public void handleSetCarriedItem(ServerboundSetCarriedItemPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ if (this.player.isImmobile()) return; // CraftBukkit
+ if (packet.getSlot() >= 0 && packet.getSlot() < Inventory.getSelectionSize()) {
+ PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getCraftPlayer(), this.player.getInventory().selected, packet.getSlot());
+ this.cserver.getPluginManager().callEvent(event);
+ if (event.isCancelled()) {
+ this.send(new ClientboundSetCarriedItemPacket(this.player.getInventory().selected));
+ this.player.resetLastActionTime();
+ return;
+ }
+ // CraftBukkit end
+ if (this.player.getInventory().selected != packet.getSlot() && this.player.getUsedItemHand() == EnumHand.MAIN_HAND) {
this.player.stopUsingItem();
}
@@ -1302,19 +1716,25 @@
this.player.resetLastActionTime();
} else {
ServerGamePacketListenerImpl.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString());
+ this.disconnect("Invalid hotbar selection (Hacking?)"); // CraftBukkit
}
}
@Override
- @Override
- public void handleChat(ServerboundChatPacket serverboundchatpacket) {
- if (isChatMessageIllegal(serverboundchatpacket.message())) {
+ public void handleChat(ServerboundChatPacket packet) {
+ // CraftBukkit start - async chat
+ // SPIGOT-3638
+ if (this.server.isStopped()) {
+ return;
+ }
+ // CraftBukkit end
+ if (isChatMessageIllegal(packet.message())) {
this.disconnect(Component.translatable("multiplayer.disconnect.illegal_characters"));
} else {
Optional<LastSeenMessages> optional = this.tryHandleChat(serverboundchatpacket.lastSeenMessages());
if (optional.isPresent()) {
- this.server.submit(() -> {
+ // this.server.submit(() -> { // CraftBukkit - async chat
PlayerChatMessage playerchatmessage;
try {
@@ -1324,15 +1744,15 @@
return;
}
- CompletableFuture<FilteredText> completablefuture = this.filterTextPacket(playerchatmessage.signedContent());
- Component component = this.server.getChatDecorator().decorate(this.player, playerchatmessage.decoratedContent());
+ CompletableFuture<FilteredText> completablefuture = this.filterTextPacket(playerchatmessage.signedContent()).thenApplyAsync(Function.identity(), this.server.chatExecutor); // CraftBukkit - async chat
+ Component ichatbasecomponent = this.server.getChatDecorator().decorate(this.player, playerchatmessage.decoratedContent());
this.chatMessageChain.append(completablefuture, (filteredtext) -> {
PlayerChatMessage playerchatmessage1 = playerchatmessage.withUnsignedContent(component).filter(filteredtext.mask());
this.broadcastChatMessage(playerchatmessage1);
});
- });
+ // }); // CraftBukkit - async chat
}
}
@@ -1348,7 +1767,13 @@
if (optional.isPresent()) {
this.server.submit(() -> {
- this.performChatCommand(serverboundchatcommandpacket, (LastSeenMessages) optional.get());
+ // CraftBukkit start - SPIGOT-7346: Prevent disconnected players from executing commands
+ if (player.hasDisconnected()) {
+ return;
+ }
+ // CraftBukkit end
+
+ this.performChatCommand(packet, (LastSeenMessages) optional.get());
this.detectRateSpam();
});
}
@@ -1356,15 +1781,28 @@
}
}
- private void performChatCommand(ServerboundChatCommandPacket serverboundchatcommandpacket, LastSeenMessages lastseenmessages) {
- ParseResults parseresults = this.parseCommand(serverboundchatcommandpacket.command());
+ private void performChatCommand(ServerboundChatCommandPacket packet, LastSeenMessages lastSeenMessages) {
+ // CraftBukkit start
+ String command = "/" + packet.command();
+ ServerGamePacketListenerImpl.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + command);
+ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(getCraftPlayer(), command, new LazyPlayerSet(server));
+ this.cserver.getPluginManager().callEvent(event);
+
+ if (event.isCancelled()) {
+ return;
+ }
+ command = event.getMessage().substring(1);
+
+ ParseResults parseresults = this.parseCommand(command);
+ // CraftBukkit end
+
Map map;
try {
- map = this.collectSignedArguments(serverboundchatcommandpacket, SignableCommand.of(parseresults), lastseenmessages);
- } catch (SignedMessageChain.DecodeException signedmessagechain_decodeexception) {
- this.handleMessageDecodeFailure(signedmessagechain_decodeexception);
+ map = (packet.command().equals(command)) ? this.collectSignedArguments(packet, SignableCommand.of(parseresults), lastSeenMessages) : Collections.emptyMap(); // CraftBukkit
+ } catch (SignedMessageChain.DecodeException signedmessagechain_a) {
+ this.handleMessageDecodeFailure(signedmessagechain_a);
return;
}
@@ -1373,7 +1811,7 @@
parseresults = Commands.mapSource(parseresults, (commandsourcestack) -> {
return commandsourcestack.withSigningContext(commandsigningcontext_signedarguments, this.chatMessageChain);
});
- this.server.getCommands().performCommand(parseresults, serverboundchatcommandpacket.command());
+ this.server.getCommands().performCommand(parseresults, command); // CraftBukkit
}
private void handleMessageDecodeFailure(SignedMessageChain.DecodeException signedmessagechain_decodeexception) {
@@ -1410,7 +1848,7 @@
private Optional<LastSeenMessages> tryHandleChat(LastSeenMessages.Update lastseenmessages_update) {
Optional<LastSeenMessages> optional = this.unpackAndApplyLastSeen(lastseenmessages_update);
- if (this.player.getChatVisibility() == ChatVisiblity.HIDDEN) {
+ if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales
this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false));
return Optional.empty();
} else {
@@ -1444,20 +1882,74 @@
return false;
}
- private PlayerChatMessage getSignedMessage(ServerboundChatPacket serverboundchatpacket, LastSeenMessages lastseenmessages) throws SignedMessageChain.DecodeException {
- SignedMessageBody signedmessagebody = new SignedMessageBody(serverboundchatpacket.message(), serverboundchatpacket.timeStamp(), serverboundchatpacket.salt(), lastseenmessages);
+ // CraftBukkit start - add method
+ public void chat(String s, PlayerChatMessage original, boolean async) {
+ if (s.isEmpty() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) {
+ return;
+ }
+ OutgoingChatMessage outgoing = OutgoingChatMessage.create(original);
return this.signedMessageDecoder.unpack(serverboundchatpacket.signature(), signedmessagebody);
}
- private void broadcastChatMessage(PlayerChatMessage playerchatmessage) {
- this.server.getPlayerList().broadcastChatMessage(playerchatmessage, this.player, ChatType.bind(ChatType.CHAT, (Entity) this.player));
+ private void handleCommand(String s) {
+ this.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + s);
+
+ CraftPlayer player = this.getCraftPlayer();
+
+ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, s, new LazyPlayerSet(server));
+ this.cserver.getPluginManager().callEvent(event);
+
+ 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;
+ }
+ }
+ // CraftBukkit end
+
+ private PlayerChatMessage getSignedMessage(ServerboundChatPacket packet, LastSeenMessages lastSeenMessages) throws SignedMessageChain.DecodeException {
+ SignedMessageBody signedmessagebody = new SignedMessageBody(packet.message(), packet.timeStamp(), packet.salt(), lastSeenMessages);
+
+ return this.signedMessageDecoder.unpack(packet.signature(), signedmessagebody);
+ }
+
+ private void broadcastChatMessage(PlayerChatMessage message) {
+ // CraftBukkit start
+ String s = message.signedContent();
+ if (s.isEmpty()) {
+ LOGGER.warn(this.player.getScoreboardName() + " tried to send an empty message");
+ } else if (getCraftPlayer().isConversing()) {
+ final String conversationInput = s;
+ this.server.processQueue.add(new Runnable() {
+ @Override
+ public void run() {
+ getCraftPlayer().acceptConversationInput(conversationInput);
+ }
+ });
+ } else if (this.player.getChatVisibility() == ChatVisiblity.SYSTEM) { // Re-add "Command Only" flag check
+ this.send(new ClientboundSystemChatPacket(Component.translatable("chat.cannotSend").withStyle(ChatFormatting.RED), false));
+ } else {
+ this.chat(s, message, true);
+ }
+ // this.server.getPlayerList().broadcastChatMessage(playerchatmessage, this.player, ChatMessageType.bind(ChatMessageType.CHAT, (Entity) this.player));
+ // CraftBukkit end
this.detectRateSpam();
}
private void detectRateSpam() {
- this.chatSpamTickCount += 20;
- if (this.chatSpamTickCount > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
+ // CraftBukkit start - replaced with thread safe throttle
+ // this.chatSpamTickCount += 20;
+ if (this.chatSpamTickCount.addAndGet(20) > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
+ // CraftBukkit end
this.disconnect(Component.translatable("disconnect.spam"));
}
@@ -1478,17 +2045,64 @@
}
@Override
- @Override
- public void handleAnimate(ServerboundSwingPacket serverboundswingpacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundswingpacket, this, this.player.serverLevel());
+ public void handleAnimate(ServerboundSwingPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ if (this.player.isImmobile()) return; // CraftBukkit
this.player.resetLastActionTime();
- this.player.swing(serverboundswingpacket.getHand());
+ // CraftBukkit start - Raytrace to look for 'rogue armswings'
+ float f1 = this.player.getXRot();
+ float f2 = this.player.getYRot();
+ double d0 = this.player.getX();
+ double d1 = this.player.getY() + (double) this.player.getEyeHeight();
+ double d2 = this.player.getZ();
+ Location origin = new Location(this.player.level().getWorld(), d0, d1, d2, f2, f1);
+
+ double d3 = player.gameMode.getGameModeForPlayer() == GameType.CREATIVE ? 5.0D : 4.5D;
+ // SPIGOT-5607: Only call interact event if no block or entity is being clicked. Use bukkit ray trace method, because it handles blocks and entities at the same time
+ // SPIGOT-7429: Make sure to call PlayerInteractEvent for spectators and non-pickable entities
+ org.bukkit.util.RayTraceResult result = this.player.level().getWorld().rayTrace(origin, origin.getDirection(), d3, org.bukkit.FluidCollisionMode.NEVER, false, 0.1, entity -> {
+ Entity handle = ((CraftEntity) entity).getHandle();
+ return entity != this.player.getBukkitEntity() && this.player.getBukkitEntity().canSee(entity) && !handle.isSpectator() && handle.isPickable() && !handle.isPassengerOfSameVehicle(player);
+ });
+ if (result == null) {
+ CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_AIR, this.player.getInventory().getSelected(), EnumHand.MAIN_HAND);
+ }
+
+ // Arm swing animation
+ PlayerAnimationEvent event = new PlayerAnimationEvent(this.getCraftPlayer(), (packet.getHand() == EnumHand.MAIN_HAND) ? PlayerAnimationType.ARM_SWING : PlayerAnimationType.OFF_ARM_SWING);
+ this.cserver.getPluginManager().callEvent(event);
+
+ if (event.isCancelled()) return;
+ // CraftBukkit end
+ this.player.swing(packet.getHand());
}
@Override
- @Override
- public void handlePlayerCommand(ServerboundPlayerCommandPacket serverboundplayercommandpacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundplayercommandpacket, this, this.player.serverLevel());
+ public void handlePlayerCommand(ServerboundPlayerCommandPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ // CraftBukkit start
+ if (this.player.isRemoved()) return;
+ switch (packet.getAction()) {
+ case PRESS_SHIFT_KEY:
+ case RELEASE_SHIFT_KEY:
+ PlayerToggleSneakEvent event = new PlayerToggleSneakEvent(this.getCraftPlayer(), packet.getAction() == ServerboundPlayerCommandPacket.EnumPlayerAction.PRESS_SHIFT_KEY);
+ this.cserver.getPluginManager().callEvent(event);
+
+ if (event.isCancelled()) {
+ return;
+ }
+ break;
+ case START_SPRINTING:
+ case STOP_SPRINTING:
+ PlayerToggleSprintEvent e2 = new PlayerToggleSprintEvent(this.getCraftPlayer(), packet.getAction() == ServerboundPlayerCommandPacket.EnumPlayerAction.START_SPRINTING);
+ this.cserver.getPluginManager().callEvent(e2);
+
+ if (e2.isCancelled()) {
+ return;
+ }
+ break;
+ }
+ // CraftBukkit end
this.player.resetLastActionTime();
Entity entity;
PlayerRideableJumping playerrideablejumping;
@@ -1569,9 +2183,15 @@
}
}
- public void sendPlayerChatMessage(PlayerChatMessage playerchatmessage, ChatType.Bound chattype_bound) {
- this.send(new ClientboundPlayerChatPacket(playerchatmessage.link().sender(), playerchatmessage.link().index(), playerchatmessage.signature(), playerchatmessage.signedBody().pack(this.messageSignatureCache), playerchatmessage.unsignedContent(), playerchatmessage.filterMask(), chattype_bound.toNetwork(this.player.level().registryAccess())));
- this.addPendingMessage(playerchatmessage);
+ public void sendPlayerChatMessage(PlayerChatMessage chatMessage, ChatType.Bound boundType) {
+ // CraftBukkit start - SPIGOT-7262: if hidden we have to send as disguised message. Query whether we should send at all (but changing this may not be expected).
+ if (!getCraftPlayer().canSee(chatMessage.link().sender())) {
+ sendDisguisedChatMessage(chatMessage.decoratedContent(), boundType);
+ return;
+ }
+ // CraftBukkit end
+ this.send(new ClientboundPlayerChatPacket(chatMessage.link().sender(), chatMessage.link().index(), chatMessage.signature(), chatMessage.signedBody().pack(this.messageSignatureCache), chatMessage.unsignedContent(), chatMessage.filterMask(), boundType.toNetwork(this.player.level().registryAccess())));
+ this.addPendingMessage(chatMessage);
}
public void sendDisguisedChatMessage(Component component, ChatType.Bound chattype_bound) {
@@ -1595,11 +2214,11 @@
}
@Override
- @Override
- public void handleInteract(ServerboundInteractPacket serverboundinteractpacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundinteractpacket, this, this.player.serverLevel());
- final ServerLevel serverlevel = this.player.serverLevel();
- final Entity entity = serverboundinteractpacket.getTarget(serverlevel);
+ public void handleInteract(ServerboundInteractPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ if (this.player.isImmobile()) return; // CraftBukkit
+ final ServerLevel worldserver = this.player.serverLevel();
+ final Entity entity = packet.getTarget(worldserver);
this.player.resetLastActionTime();
this.player.setShiftKeyDown(serverboundinteractpacket.isUsingSecondaryAction());
@@ -1610,16 +2229,54 @@
AABB aabb = entity.getBoundingBox();
- if (aabb.distanceToSqr(this.player.getEyePosition()) < ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) {
- serverboundinteractpacket.dispatch(new ServerboundInteractPacket.Handler() {
- private void performInteraction(InteractionHand interactionhand, ServerGamePacketListenerImpl.EntityInteraction servergamepacketlistenerimpl_entityinteraction) {
- ItemStack itemstack = ServerGamePacketListenerImpl.this.player.getItemInHand(interactionhand);
+ if (axisalignedbb.distanceToSqr(this.player.getEyePosition()) < ServerGamePacketListenerImpl.MAX_INTERACTION_DISTANCE) {
+ packet.dispatch(new ServerboundInteractPacket.Handler() {
+ private void performInteraction(EnumHand enumhand, ServerGamePacketListenerImpl.EntityInteraction playerconnection_a, PlayerInteractEntityEvent event) { // CraftBukkit
+ ItemStack itemstack = ServerGamePacketListenerImpl.this.player.getItemInHand(enumhand);
if (itemstack.isItemEnabled(serverlevel.enabledFeatures())) {
ItemStack itemstack1 = itemstack.copy();
- InteractionResult interactionresult = servergamepacketlistenerimpl_entityinteraction.run(ServerGamePacketListenerImpl.this.player, entity, interactionhand);
+ // CraftBukkit start
+ ItemStack itemInHand = ServerGamePacketListenerImpl.this.player.getItemInHand(enumhand);
+ boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEAD && entity instanceof Mob;
+ Item origItem = player.getInventory().getSelected() == null ? null : player.getInventory().getSelected().getItem();
- if (interactionresult.consumesAction()) {
+ cserver.getPluginManager().callEvent(event);
+
+ // Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
+ if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || player.getInventory().getSelected() == null || player.getInventory().getSelected().getItem() != origItem)) {
+ send(new ClientboundAddEntityPacket(entity));
+ player.containerMenu.sendAllDataToRemote();
+ }
+
+ if (triggerLeashUpdate && (event.isCancelled() || player.getInventory().getSelected() == null || player.getInventory().getSelected().getItem() != origItem)) {
+ // Refresh the current leash state
+ send(new ClientboundSetEntityLinkPacket(entity, ((Mob) entity).getLeashHolder()));
+ }
+
+ if (event.isCancelled() || player.getInventory().getSelected() == null || player.getInventory().getSelected().getItem() != origItem) {
+ // Refresh the current entity metadata
+ entity.getEntityData().refresh(player);
+ // SPIGOT-7136 - Allays
+ if (entity instanceof Allay) {
+ send(new ClientboundSetEquipmentPacket(entity.getId(), Arrays.stream(net.minecraft.world.entity.EquipmentSlot.values()).map((slot) -> Pair.of(slot, ((LivingEntity) entity).getItemBySlot(slot).copy())).collect(Collectors.toList())));
+ player.containerMenu.sendAllDataToRemote();
+ }
+ }
+
+ if (event.isCancelled()) {
+ return;
+ }
+ // CraftBukkit end
+ InteractionResult enuminteractionresult = playerconnection_a.run(ServerGamePacketListenerImpl.this.player, entity, enumhand);
+
+ // CraftBukkit start
+ if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) {
+ player.containerMenu.sendAllDataToRemote();
+ }
+ // CraftBukkit end
+
+ if (enuminteractionresult.consumesAction()) {
CriteriaTriggers.PLAYER_INTERACTED_WITH_ENTITY.trigger(ServerGamePacketListenerImpl.this.player, itemstack1, entity);
if (interactionresult.shouldSwing()) {
ServerGamePacketListenerImpl.this.player.swing(interactionhand, true);
@@ -1630,27 +2287,31 @@
}
@Override
- @Override
- public void onInteraction(InteractionHand interactionhand) {
- this.performInteraction(interactionhand, Player::interactOn);
+ public void onInteraction(EnumHand hand) {
+ this.performInteraction(hand, net.minecraft.world.entity.player.Player::interactOn, new PlayerInteractEntityEvent(getCraftPlayer(), entity.getBukkitEntity(), (hand == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND)); // CraftBukkit
}
@Override
- @Override
- public void onInteraction(InteractionHand interactionhand, Vec3 vec3) {
- this.performInteraction(interactionhand, (serverplayer, entity1, interactionhand1) -> {
- return entity1.interactAt(serverplayer, vec3, interactionhand1);
- });
+ public void onInteraction(EnumHand hand, Vec3 interactionLocation) {
+ this.performInteraction(hand, (entityplayer, entity1, enumhand1) -> {
+ return entity1.interactAt(entityplayer, interactionLocation, enumhand1);
+ }, new PlayerInteractAtEntityEvent(getCraftPlayer(), entity.getBukkitEntity(), new org.bukkit.util.Vector(interactionLocation.x, interactionLocation.y, interactionLocation.z), (hand == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND)); // CraftBukkit
}
@Override
@Override
public void onAttack() {
- if (!(entity instanceof ItemEntity) && !(entity instanceof ExperienceOrb) && !(entity instanceof AbstractArrow) && entity != ServerGamePacketListenerImpl.this.player) {
- ItemStack itemstack = ServerGamePacketListenerImpl.this.player.getItemInHand(InteractionHand.MAIN_HAND);
+ // CraftBukkit
+ if (!(entity instanceof ItemEntity) && !(entity instanceof ExperienceOrb) && !(entity instanceof AbstractArrow) && (entity != ServerGamePacketListenerImpl.this.player || player.isSpectator())) {
+ ItemStack itemstack = ServerGamePacketListenerImpl.this.player.getItemInHand(EnumHand.MAIN_HAND);
if (itemstack.isItemEnabled(serverlevel.enabledFeatures())) {
ServerGamePacketListenerImpl.this.player.attack(entity);
+ // CraftBukkit start
+ if (!itemstack.isEmpty() && itemstack.getCount() <= -1) {
+ player.containerMenu.sendAllDataToRemote();
+ }
+ // CraftBukkit end
}
} else {
ServerGamePacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.invalid_entity_attacked"));
@@ -1695,19 +2354,23 @@
}
@Override
- @Override
- public void handleContainerClose(ServerboundContainerClosePacket serverboundcontainerclosepacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundcontainerclosepacket, this, this.player.serverLevel());
+ public void handleContainerClose(ServerboundContainerClosePacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+
+ if (this.player.isImmobile()) return; // CraftBukkit
+ CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit
+
this.player.doCloseContainer();
}
@Override
- @Override
- public void handleContainerClick(ServerboundContainerClickPacket serverboundcontainerclickpacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundcontainerclickpacket, this, this.player.serverLevel());
+ public void handleContainerClick(ServerboundContainerClickPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ if (this.player.isImmobile()) return; // CraftBukkit
this.player.resetLastActionTime();
- if (this.player.containerMenu.containerId == serverboundcontainerclickpacket.getContainerId()) {
- if (this.player.isSpectator()) {
+ if (this.player.containerMenu.containerId == packet.getContainerId() && this.player.containerMenu.stillValid(this.player)) { // CraftBukkit
+ boolean cancelled = this.player.isSpectator(); // CraftBukkit - see below if
+ if (false/*this.player.isSpectator()*/) { // CraftBukkit
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);
@@ -1720,9 +2383,286 @@
boolean flag = serverboundcontainerclickpacket.getStateId() != this.player.containerMenu.getStateId();
this.player.containerMenu.suppressRemoteUpdates();
- this.player.containerMenu.clicked(i, serverboundcontainerclickpacket.getButtonNum(), serverboundcontainerclickpacket.getClickType(), this.player);
- ObjectIterator objectiterator = Int2ObjectMaps.fastIterable(serverboundcontainerclickpacket.getChangedSlots()).iterator();
+ // CraftBukkit start - Call InventoryClickEvent
+ if (packet.getSlotNum() < -1 && packet.getSlotNum() != -999) {
+ return;
+ }
+ InventoryView inventory = this.player.containerMenu.getBukkitView();
+ SlotType type = inventory.getSlotType(packet.getSlotNum());
+
+ InventoryClickEvent event;
+ ClickType click = ClickType.UNKNOWN;
+ InventoryAction action = InventoryAction.UNKNOWN;
+
+ ItemStack itemstack = ItemStack.EMPTY;
+
+ switch (packet.getClickType()) {
+ case PICKUP:
+ if (packet.getButtonNum() == 0) {
+ click = ClickType.LEFT;
+ } else if (packet.getButtonNum() == 1) {
+ click = ClickType.RIGHT;
+ }
+ if (packet.getButtonNum() == 0 || packet.getButtonNum() == 1) {
+ action = InventoryAction.NOTHING; // Don't want to repeat ourselves
+ if (packet.getSlotNum() == -999) {
+ if (!player.containerMenu.getCarried().isEmpty()) {
+ action = packet.getButtonNum() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR;
+ }
+ } else if (packet.getSlotNum() < 0) {
+ action = InventoryAction.NOTHING;
+ } else {
+ Slot slot = this.player.containerMenu.getSlot(packet.getSlotNum());
+ if (slot != null) {
+ ItemStack clickedItem = slot.getItem();
+ ItemStack cursor = player.containerMenu.getCarried();
+ if (clickedItem.isEmpty()) {
+ if (!cursor.isEmpty()) {
+ action = packet.getButtonNum() == 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_ONE;
+ }
+ } else if (slot.mayPickup(player)) {
+ if (cursor.isEmpty()) {
+ action = packet.getButtonNum() == 0 ? InventoryAction.PICKUP_ALL : InventoryAction.PICKUP_HALF;
+ } else if (slot.mayPlace(cursor)) {
+ if (ItemStack.isSameItemSameTags(clickedItem, cursor)) {
+ int toPlace = packet.getButtonNum() == 0 ? cursor.getCount() : 1;
+ toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.getCount());
+ toPlace = Math.min(toPlace, slot.container.getMaxStackSize() - clickedItem.getCount());
+ if (toPlace == 1) {
+ action = InventoryAction.PLACE_ONE;
+ } else if (toPlace == cursor.getCount()) {
+ action = InventoryAction.PLACE_ALL;
+ } else if (toPlace < 0) {
+ action = toPlace != -1 ? InventoryAction.PICKUP_SOME : InventoryAction.PICKUP_ONE; // this happens with oversized stacks
+ } else if (toPlace != 0) {
+ action = InventoryAction.PLACE_SOME;
+ }
+ } else if (cursor.getCount() <= slot.getMaxStackSize()) {
+ action = InventoryAction.SWAP_WITH_CURSOR;
+ }
+ } else if (ItemStack.isSameItemSameTags(cursor, clickedItem)) {
+ if (clickedItem.getCount() >= 0) {
+ if (clickedItem.getCount() + cursor.getCount() <= cursor.getMaxStackSize()) {
+ // As of 1.5, this is result slots only
+ action = InventoryAction.PICKUP_ALL;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ // TODO check on updates
+ case QUICK_MOVE:
+ if (packet.getButtonNum() == 0) {
+ click = ClickType.SHIFT_LEFT;
+ } else if (packet.getButtonNum() == 1) {
+ click = ClickType.SHIFT_RIGHT;
+ }
+ if (packet.getButtonNum() == 0 || packet.getButtonNum() == 1) {
+ if (packet.getSlotNum() < 0) {
+ action = InventoryAction.NOTHING;
+ } else {
+ Slot slot = this.player.containerMenu.getSlot(packet.getSlotNum());
+ if (slot != null && slot.mayPickup(this.player) && slot.hasItem()) {
+ action = InventoryAction.MOVE_TO_OTHER_INVENTORY;
+ } else {
+ action = InventoryAction.NOTHING;
+ }
+ }
+ }
+ break;
+ case SWAP:
+ if ((packet.getButtonNum() >= 0 && packet.getButtonNum() < 9) || packet.getButtonNum() == 40) {
+ click = (packet.getButtonNum() == 40) ? ClickType.SWAP_OFFHAND : ClickType.NUMBER_KEY;
+ Slot clickedSlot = this.player.containerMenu.getSlot(packet.getSlotNum());
+ if (clickedSlot.mayPickup(player)) {
+ ItemStack hotbar = this.player.getInventory().getItem(packet.getButtonNum());
+ boolean canCleanSwap = hotbar.isEmpty() || (clickedSlot.container == player.getInventory() && clickedSlot.mayPlace(hotbar)); // the slot will accept the hotbar item
+ if (clickedSlot.hasItem()) {
+ if (canCleanSwap) {
+ action = InventoryAction.HOTBAR_SWAP;
+ } else {
+ action = InventoryAction.HOTBAR_MOVE_AND_READD;
+ }
+ } else if (!clickedSlot.hasItem() && !hotbar.isEmpty() && clickedSlot.mayPlace(hotbar)) {
+ action = InventoryAction.HOTBAR_SWAP;
+ } else {
+ action = InventoryAction.NOTHING;
+ }
+ } else {
+ action = InventoryAction.NOTHING;
+ }
+ }
+ break;
+ case CLONE:
+ if (packet.getButtonNum() == 2) {
+ click = ClickType.MIDDLE;
+ if (packet.getSlotNum() < 0) {
+ action = InventoryAction.NOTHING;
+ } else {
+ Slot slot = this.player.containerMenu.getSlot(packet.getSlotNum());
+ if (slot != null && slot.hasItem() && player.getAbilities().instabuild && player.containerMenu.getCarried().isEmpty()) {
+ action = InventoryAction.CLONE_STACK;
+ } else {
+ action = InventoryAction.NOTHING;
+ }
+ }
+ } else {
+ click = ClickType.UNKNOWN;
+ action = InventoryAction.UNKNOWN;
+ }
+ break;
+ case THROW:
+ if (packet.getSlotNum() >= 0) {
+ if (packet.getButtonNum() == 0) {
+ click = ClickType.DROP;
+ Slot slot = this.player.containerMenu.getSlot(packet.getSlotNum());
+ if (slot != null && slot.hasItem() && slot.mayPickup(player) && !slot.getItem().isEmpty() && slot.getItem().getItem() != Item.byBlock(Blocks.AIR)) {
+ action = InventoryAction.DROP_ONE_SLOT;
+ } else {
+ action = InventoryAction.NOTHING;
+ }
+ } else if (packet.getButtonNum() == 1) {
+ click = ClickType.CONTROL_DROP;
+ Slot slot = this.player.containerMenu.getSlot(packet.getSlotNum());
+ if (slot != null && slot.hasItem() && slot.mayPickup(player) && !slot.getItem().isEmpty() && slot.getItem().getItem() != Item.byBlock(Blocks.AIR)) {
+ action = InventoryAction.DROP_ALL_SLOT;
+ } else {
+ action = InventoryAction.NOTHING;
+ }
+ }
+ } else {
+ // Sane default (because this happens when they are holding nothing. Don't ask why.)
+ click = ClickType.LEFT;
+ if (packet.getButtonNum() == 1) {
+ click = ClickType.RIGHT;
+ }
+ action = InventoryAction.NOTHING;
+ }
+ break;
+ case QUICK_CRAFT:
+ this.player.containerMenu.clicked(packet.getSlotNum(), packet.getButtonNum(), packet.getClickType(), this.player);
+ break;
+ case PICKUP_ALL:
+ click = ClickType.DOUBLE_CLICK;
+ action = InventoryAction.NOTHING;
+ if (packet.getSlotNum() >= 0 && !this.player.containerMenu.getCarried().isEmpty()) {
+ ItemStack cursor = this.player.containerMenu.getCarried();
+ action = InventoryAction.NOTHING;
+ // Quick check for if we have any of the item
+ if (inventory.getTopInventory().contains(CraftMagicNumbers.getMaterial(cursor.getItem())) || inventory.getBottomInventory().contains(CraftMagicNumbers.getMaterial(cursor.getItem()))) {
+ action = InventoryAction.COLLECT_TO_CURSOR;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (packet.getClickType() != InventoryClickType.QUICK_CRAFT) {
+ if (click == ClickType.NUMBER_KEY) {
+ event = new InventoryClickEvent(inventory, type, packet.getSlotNum(), click, action, packet.getButtonNum());
+ } else {
+ event = new InventoryClickEvent(inventory, type, packet.getSlotNum(), click, action);
+ }
+
+ org.bukkit.inventory.Inventory top = inventory.getTopInventory();
+ if (packet.getSlotNum() == 0 && top instanceof CraftingInventory) {
+ org.bukkit.inventory.Recipe recipe = ((CraftingInventory) top).getRecipe();
+ if (recipe != null) {
+ if (click == ClickType.NUMBER_KEY) {
+ event = new CraftItemEvent(recipe, inventory, type, packet.getSlotNum(), click, action, packet.getButtonNum());
+ } else {
+ event = new CraftItemEvent(recipe, inventory, type, packet.getSlotNum(), click, action);
+ }
+ }
+ }
+
+ if (packet.getSlotNum() == 3 && top instanceof SmithingInventory) {
+ org.bukkit.inventory.ItemStack result = ((SmithingInventory) top).getResult();
+ if (result != null) {
+ if (click == ClickType.NUMBER_KEY) {
+ event = new SmithItemEvent(inventory, type, packet.getSlotNum(), click, action, packet.getButtonNum());
+ } else {
+ event = new SmithItemEvent(inventory, type, packet.getSlotNum(), click, action);
+ }
+ }
+ }
+
+ event.setCancelled(cancelled);
+ AbstractContainerMenu oldContainer = this.player.containerMenu; // SPIGOT-1224
+ cserver.getPluginManager().callEvent(event);
+ if (this.player.containerMenu != oldContainer) {
+ return;
+ }
+
+ switch (event.getResult()) {
+ case ALLOW:
+ case DEFAULT:
+ this.player.containerMenu.clicked(i, packet.getButtonNum(), packet.getClickType(), this.player);
+ break;
+ case DENY:
+ /* Needs enum constructor in InventoryAction
+ if (action.modifiesOtherSlots()) {
+
+ } else {
+ if (action.modifiesCursor()) {
+ this.player.playerConnection.sendPacket(new Packet103SetSlot(-1, -1, this.player.inventory.getCarried()));
+ }
+ if (action.modifiesClicked()) {
+ this.player.playerConnection.sendPacket(new Packet103SetSlot(this.player.activeContainer.windowId, packet102windowclick.slot, this.player.activeContainer.getSlot(packet102windowclick.slot).getItem()));
+ }
+ }*/
+ switch (action) {
+ // Modified other slots
+ case PICKUP_ALL:
+ case MOVE_TO_OTHER_INVENTORY:
+ case HOTBAR_MOVE_AND_READD:
+ case HOTBAR_SWAP:
+ case COLLECT_TO_CURSOR:
+ case UNKNOWN:
+ this.player.containerMenu.sendAllDataToRemote();
+ break;
+ // Modified cursor and clicked
+ case PICKUP_SOME:
+ case PICKUP_HALF:
+ case PICKUP_ONE:
+ case PLACE_ALL:
+ case PLACE_SOME:
+ case PLACE_ONE:
+ case SWAP_WITH_CURSOR:
+ this.player.connection.send(new ClientboundContainerSetSlotPacket(-1, -1, this.player.inventoryMenu.incrementStateId(), this.player.containerMenu.getCarried()));
+ this.player.connection.send(new ClientboundContainerSetSlotPacket(this.player.containerMenu.containerId, this.player.inventoryMenu.incrementStateId(), packet.getSlotNum(), this.player.containerMenu.getSlot(packet.getSlotNum()).getItem()));
+ break;
+ // Modified clicked only
+ case DROP_ALL_SLOT:
+ case DROP_ONE_SLOT:
+ this.player.connection.send(new ClientboundContainerSetSlotPacket(this.player.containerMenu.containerId, this.player.inventoryMenu.incrementStateId(), packet.getSlotNum(), this.player.containerMenu.getSlot(packet.getSlotNum()).getItem()));
+ break;
+ // Modified cursor only
+ case DROP_ALL_CURSOR:
+ case DROP_ONE_CURSOR:
+ case CLONE_STACK:
+ this.player.connection.send(new ClientboundContainerSetSlotPacket(-1, -1, this.player.inventoryMenu.incrementStateId(), this.player.containerMenu.getCarried()));
+ break;
+ // Nothing
+ case NOTHING:
+ break;
+ }
+ }
+
+ if (event instanceof CraftItemEvent || event instanceof SmithItemEvent) {
+ // Need to update the inventory on crafting to
+ // correctly support custom recipes
+ player.containerMenu.sendAllDataToRemote();
+ }
+ }
+ // CraftBukkit end
+ ObjectIterator objectiterator = Int2ObjectMaps.fastIterable(packet.getChangedSlots()).iterator();
+
while (objectiterator.hasNext()) {
Entry<ItemStack> entry = (Entry) objectiterator.next();
@@ -1751,17 +2690,26 @@
if (!this.player.containerMenu.stillValid(this.player)) {
ServerGamePacketListenerImpl.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu);
} else {
- this.server.getRecipeManager().byKey(serverboundplacerecipepacket.getRecipe()).ifPresent((recipeholder) -> {
- ((RecipeBookMenu) this.player.containerMenu).handlePlacement(serverboundplacerecipepacket.isShiftDown(), recipeholder, this.player);
+ // CraftBukkit start - implement PlayerRecipeBookClickEvent
+ org.bukkit.inventory.Recipe recipe = this.cserver.getRecipe(CraftNamespacedKey.fromMinecraft(packet.getRecipe()));
+ if (recipe == null) {
+ return;
+ }
+ org.bukkit.event.player.PlayerRecipeBookClickEvent event = CraftEventFactory.callRecipeBookClickEvent(this.player, recipe, packet.isShiftDown());
+
+ // Cast to keyed should be safe as the recipe will never be a MerchantRecipe.
+ this.server.getRecipeManager().byKey(CraftNamespacedKey.toMinecraft(((org.bukkit.Keyed) event.getRecipe()).getKey())).ifPresent((recipeholder) -> {
+ ((RecipeBookMenu) this.player.containerMenu).handlePlacement(event.isShiftClick(), recipeholder, this.player);
});
+ // CraftBukkit end
}
}
}
@Override
- @Override
- public void handleContainerButtonClick(ServerboundContainerButtonClickPacket serverboundcontainerbuttonclickpacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundcontainerbuttonclickpacket, this, this.player.serverLevel());
+ public void handleContainerButtonClick(ServerboundContainerButtonClickPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ if (this.player.isImmobile()) return; // CraftBukkit
this.player.resetLastActionTime();
if (this.player.containerMenu.containerId == serverboundcontainerbuttonclickpacket.getContainerId() && !this.player.isSpectator()) {
if (!this.player.containerMenu.stillValid(this.player)) {
@@ -1805,7 +2752,44 @@
boolean flag1 = serverboundsetcreativemodeslotpacket.getSlotNum() >= 1 && serverboundsetcreativemodeslotpacket.getSlotNum() <= 45;
boolean flag2 = itemstack.isEmpty() || itemstack.getDamageValue() >= 0 && itemstack.getCount() <= 64 && !itemstack.isEmpty();
+ if (flag || (flag1 && !ItemStack.matches(this.player.inventoryMenu.getSlot(packet.getSlotNum()).getItem(), packet.getItem()))) { // Insist on valid slot
+ // CraftBukkit start - Call click event
+ InventoryView inventory = this.player.inventoryMenu.getBukkitView();
+ org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packet.getItem());
+ SlotType type = SlotType.QUICKBAR;
+ if (flag) {
+ type = SlotType.OUTSIDE;
+ } else if (packet.getSlotNum() < 36) {
+ if (packet.getSlotNum() >= 5 && packet.getSlotNum() < 9) {
+ type = SlotType.ARMOR;
+ } else {
+ type = SlotType.CONTAINER;
+ }
+ }
+ InventoryCreativeEvent event = new InventoryCreativeEvent(inventory, type, flag ? -999 : packet.getSlotNum(), item);
+ cserver.getPluginManager().callEvent(event);
+
+ itemstack = CraftItemStack.asNMSCopy(event.getCursor());
+
+ switch (event.getResult()) {
+ case ALLOW:
+ // Plugin cleared the id / stacksize checks
+ flag2 = true;
+ break;
+ case DEFAULT:
+ break;
+ case DENY:
+ // Reset the slot
+ if (packet.getSlotNum() >= 0) {
+ this.player.connection.send(new ClientboundContainerSetSlotPacket(this.player.inventoryMenu.containerId, this.player.inventoryMenu.incrementStateId(), packet.getSlotNum(), this.player.inventoryMenu.getSlot(packet.getSlotNum()).getItem()));
+ this.player.connection.send(new ClientboundContainerSetSlotPacket(-1, this.player.inventoryMenu.incrementStateId(), -1, ItemStack.EMPTY));
+ }
+ return;
+ }
+ }
+ // CraftBukkit end
+
if (flag1 && flag2) {
this.player.inventoryMenu.getSlot(serverboundsetcreativemodeslotpacket.getSlotNum()).setByPlayer(itemstack);
this.player.inventoryMenu.broadcastChanges();
@@ -1827,7 +2810,8 @@
}, this.server);
}
- private void updateSignText(ServerboundSignUpdatePacket serverboundsignupdatepacket, List<FilteredText> list) {
+ private void updateSignText(ServerboundSignUpdatePacket packet, List<FilteredText> filteredText) {
+ if (this.player.isImmobile()) return; // CraftBukkit
this.player.resetLastActionTime();
ServerLevel serverlevel = this.player.serverLevel();
BlockPos blockpos = serverboundsignupdatepacket.getPos();
@@ -1847,10 +2831,19 @@
}
@Override
- @Override
- public void handlePlayerAbilities(ServerboundPlayerAbilitiesPacket serverboundplayerabilitiespacket) {
- PacketUtils.ensureRunningOnSameThread(serverboundplayerabilitiespacket, this, this.player.serverLevel());
- this.player.getAbilities().flying = serverboundplayerabilitiespacket.isFlying() && this.player.getAbilities().mayfly;
+ public void handlePlayerAbilities(ServerboundPlayerAbilitiesPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ // CraftBukkit start
+ if (this.player.getAbilities().mayfly && this.player.getAbilities().flying != packet.isFlying()) {
+ PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.player.getBukkitEntity(), packet.isFlying());
+ this.cserver.getPluginManager().callEvent(event);
+ if (!event.isCancelled()) {
+ this.player.getAbilities().flying = packet.isFlying(); // Actually set the player's flying status
+ } else {
+ this.player.onUpdateAbilities(); // Tell the player their ability was reverted
+ }
+ }
+ // CraftBukkit end
}
@Override
@@ -1914,7 +2902,7 @@
if (!this.waitingForSwitchToConfig) {
throw new IllegalStateException("Client acknowledged config, but none was requested");
} else {
- this.connection.setListener(new ServerConfigurationPacketListenerImpl(this.server, this.connection, this.createCookie(this.player.clientInformation())));
+ this.connection.setListener(new ServerConfigurationPacketListenerImpl(this.server, this.connection, this.createCookie(this.player.clientInformation()), this.player)); // CraftBukkit
}
}