Commits

md_5 authored ead719a65bf
SPIGOT-7136: Cancelling PlayerInteractEntityEvent with the Allay desyncs
No tags

nms-patches/net/minecraft/server/network/PlayerConnection.patch

Modified
1 1 --- a/net/minecraft/server/network/PlayerConnection.java
2 2 +++ b/net/minecraft/server/network/PlayerConnection.java
3 -@@ -184,6 +184,63 @@
3 +@@ -184,6 +184,67 @@
4 4 import org.apache.commons.lang3.StringUtils;
5 5 import org.slf4j.Logger;
6 6
7 7 +// CraftBukkit start
8 ++import java.util.Arrays;
8 9 +import java.util.concurrent.ExecutionException;
9 10 +import java.util.concurrent.atomic.AtomicInteger;
10 11 +import net.minecraft.network.chat.OutgoingPlayerChatMessage;
11 12 +import net.minecraft.network.protocol.game.PacketPlayOutAttachEntity;
13 ++import net.minecraft.network.protocol.game.PacketPlayOutEntityEquipment;
12 14 +import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
13 15 +import net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity;
14 16 +import net.minecraft.network.protocol.game.PacketPlayOutSpawnPosition;
15 17 +import net.minecraft.world.entity.EntityInsentient;
16 18 +import net.minecraft.world.entity.animal.Bucketable;
17 19 +import net.minecraft.world.entity.EntityLiving;
20 ++import net.minecraft.world.entity.EnumItemSlot;
21 ++import net.minecraft.world.entity.animal.allay.Allay;
18 22 +import net.minecraft.world.inventory.InventoryClickType;
19 23 +import net.minecraft.world.inventory.Slot;
20 24 +import net.minecraft.world.item.crafting.IRecipe;
21 25 +import net.minecraft.world.level.RayTrace;
22 26 +import net.minecraft.world.phys.MovingObjectPosition;
23 27 +import org.bukkit.Location;
24 28 +import org.bukkit.craftbukkit.entity.CraftPlayer;
25 29 +import org.bukkit.craftbukkit.event.CraftEventFactory;
26 30 +import org.bukkit.craftbukkit.inventory.CraftItemStack;
27 31 +import org.bukkit.craftbukkit.util.CraftChatMessage;
57 61 +import org.bukkit.event.player.PlayerToggleSprintEvent;
58 62 +import org.bukkit.inventory.CraftingInventory;
59 63 +import org.bukkit.inventory.EquipmentSlot;
60 64 +import org.bukkit.inventory.InventoryView;
61 65 +import org.bukkit.inventory.SmithingInventory;
62 66 +// CraftBukkit end
63 67 +
64 68 public class PlayerConnection implements ServerPlayerConnection, TickablePacketListener, PacketListenerPlayIn {
65 69
66 70 static final Logger LOGGER = LogUtils.getLogger();
67 -@@ -199,7 +256,9 @@
71 +@@ -199,7 +260,9 @@
68 72 private long keepAliveTime;
69 73 private boolean keepAlivePending;
70 74 private long keepAliveChallenge;
71 75 - private int chatSpamTickCount;
72 76 + // CraftBukkit start - multithreaded fields
73 77 + private final AtomicInteger chatSpamTickCount = new AtomicInteger();
74 78 + // CraftBukkit end
75 79 private int dropSpamTickCount;
76 80 private double firstGoodX;
77 81 private double firstGoodY;
78 -@@ -250,8 +309,32 @@
82 +@@ -250,8 +313,32 @@
79 83 this.signedMessageDecoder = SignedMessageChain.a.UNSIGNED;
80 84 }
81 85
82 86 - this.chatMessageChain = new FutureChain(minecraftserver);
83 87 + this.chatMessageChain = new FutureChain(minecraftserver.chatExecutor); // CraftBukkit - async chat
84 88 +
85 89 + // CraftBukkit start - add fields and methods
86 90 + this.cserver = minecraftserver.server;
87 91 + }
88 92 +
102 106 + private float lastYaw = Float.MAX_VALUE;
103 107 + private boolean justTeleported = false;
104 108 +
105 109 + public CraftPlayer getCraftPlayer() {
106 110 + return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity();
107 111 }
108 112 + // CraftBukkit end
109 113
110 114 @Override
111 115 public void tick() {
112 -@@ -306,7 +389,7 @@
116 +@@ -306,7 +393,7 @@
113 117 this.server.getProfiler().push("keepAlive");
114 118 long i = SystemUtils.getMillis();
115 119
116 120 - if (i - this.keepAliveTime >= 15000L) {
117 121 + if (i - this.keepAliveTime >= 25000L) { // CraftBukkit
118 122 if (this.keepAlivePending) {
119 123 this.disconnect(IChatBaseComponent.translatable("disconnect.timeout"));
120 124 } else {
121 -@@ -318,15 +401,21 @@
125 +@@ -318,15 +405,21 @@
122 126 }
123 127
124 128 this.server.getProfiler().pop();
125 129 + // CraftBukkit start
126 130 + for (int spam; (spam = this.chatSpamTickCount.get()) > 0 && !chatSpamTickCount.compareAndSet(spam, spam - 1); ) ;
127 131 + /* Use thread-safe field access instead
128 132 if (this.chatSpamTickCount > 0) {
129 133 --this.chatSpamTickCount;
130 134 }
131 135 + */
132 136 + // CraftBukkit end
133 137
134 138 if (this.dropSpamTickCount > 0) {
135 139 --this.dropSpamTickCount;
136 140 }
137 141
138 142 if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && SystemUtils.getMillis() - this.player.getLastActionTime() > (long) (this.server.getPlayerIdleTimeout() * 1000 * 60)) {
139 143 + this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854
140 144 this.disconnect(IChatBaseComponent.translatable("multiplayer.disconnect.idling"));
141 145 }
142 146
143 -@@ -351,16 +440,67 @@
147 +@@ -351,16 +444,67 @@
144 148 return this.server.isSingleplayerOwner(this.player.getGameProfile());
145 149 }
146 150
147 151 + // CraftBukkit start
148 152 + @Deprecated
149 153 public void disconnect(IChatBaseComponent ichatbasecomponent) {
150 154 + disconnect(CraftChatMessage.fromComponent(ichatbasecomponent));
151 155 + }
152 156 + // CraftBukkit end
153 157 +
202 206 MinecraftServer minecraftserver = this.server;
203 207 NetworkManager networkmanager = this.connection;
204 208
205 209 Objects.requireNonNull(this.connection);
206 210 - minecraftserver.executeBlocking(networkmanager::handleDisconnection);
207 211 + // CraftBukkit - Don't wait
208 212 + minecraftserver.wrapRunnable(networkmanager::handleDisconnection);
209 213 }
210 214
211 215 private <T, R> CompletableFuture<R> filterTextPacket(T t0, BiFunction<ITextFilter, T, CompletableFuture<R>> bifunction) {
212 -@@ -424,7 +564,34 @@
216 +@@ -424,7 +568,34 @@
213 217 double d9 = entity.getDeltaMovement().lengthSqr();
214 218 double d10 = d6 * d6 + d7 * d7 + d8 * d8;
215 219
216 220 - if (d10 - d9 > 100.0D && !this.isSingleplayerOwner()) {
217 221 +
218 222 + // CraftBukkit start - handle custom speeds and skipped ticks
219 223 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
220 224 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
221 225 + this.lastTick = (int) (System.currentTimeMillis() / 50);
222 226 +
238 242 + } else {
239 243 + speed = player.getAbilities().walkingSpeed * 10f;
240 244 + }
241 245 + speed *= 2f; // TODO: Get the speed of the vehicle instead of the player
242 246 +
243 247 + if (d10 - d9 > Math.max(100.0D, Math.pow((double) (10.0F * (float) i * speed), 2)) && !this.isSingleplayerOwner()) {
244 248 + // CraftBukkit end
245 249 PlayerConnection.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", new Object[]{entity.getName().getString(), this.player.getName().getString(), d6, d7, d8});
246 250 this.connection.send(new PacketPlayOutVehicleMove(entity));
247 251 return;
248 -@@ -456,14 +623,72 @@
252 +@@ -456,14 +627,72 @@
249 253 }
250 254
251 255 entity.absMoveTo(d3, d4, d5, f, f1);
252 256 + player.absMoveTo(d3, d4, d5, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
253 257 boolean flag3 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
254 258
255 259 if (flag && (flag2 || !flag3)) {
256 260 entity.absMoveTo(d0, d1, d2, f, f1);
257 261 + player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
258 262 this.connection.send(new PacketPlayOutVehicleMove(entity));
311 315 + this.justTeleported = false;
312 316 + return;
313 317 + }
314 318 + }
315 319 + }
316 320 + // CraftBukkit end
317 321 +
318 322 this.player.getLevel().getChunkSource().move(this.player);
319 323 this.player.checkMovementStatistics(this.player.getX() - d0, this.player.getY() - d1, this.player.getZ() - d2);
320 324 this.clientVehicleIsFloating = d11 >= -0.03125D && !flag1 && !this.server.isFlightAllowed() && !entity.isNoGravity() && this.noBlocksAround(entity);
321 -@@ -497,6 +722,7 @@
325 +@@ -497,6 +726,7 @@
322 326 }
323 327
324 328 this.awaitingPositionFromClient = null;
325 329 + this.player.getLevel().getChunkSource().move(this.player); // CraftBukkit
326 330 }
327 331
328 332 }
329 -@@ -504,7 +730,7 @@
333 +@@ -504,7 +734,7 @@
330 334 @Override
331 335 public void handleRecipeBookSeenRecipePacket(PacketPlayInRecipeDisplayed packetplayinrecipedisplayed) {
332 336 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinrecipedisplayed, this, this.player.getLevel());
333 337 - Optional optional = this.server.getRecipeManager().byKey(packetplayinrecipedisplayed.getRecipe());
334 338 + Optional<? extends IRecipe<?>> optional = this.server.getRecipeManager().byKey(packetplayinrecipedisplayed.getRecipe()); // CraftBukkit - decompile error
335 339 RecipeBookServer recipebookserver = this.player.getRecipeBook();
336 340
337 341 Objects.requireNonNull(recipebookserver);
338 -@@ -534,6 +760,12 @@
342 +@@ -534,6 +764,12 @@
339 343 @Override
340 344 public void handleCustomCommandSuggestions(PacketPlayInTabComplete packetplayintabcomplete) {
341 345 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayintabcomplete, this, this.player.getLevel());
342 346 + // CraftBukkit start
343 347 + if (chatSpamTickCount.addAndGet(1) > 500 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
344 348 + this.disconnect(IChatBaseComponent.translatable("disconnect.spam"));
345 349 + return;
346 350 + }
347 351 + // CraftBukkit end
348 352 StringReader stringreader = new StringReader(packetplayintabcomplete.getCommand());
349 353
350 354 if (stringreader.canRead() && stringreader.peek() == '/') {
351 -@@ -543,6 +775,7 @@
355 +@@ -543,6 +779,7 @@
352 356 ParseResults<CommandListenerWrapper> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
353 357
354 358 this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
355 359 + if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
356 360 this.connection.send(new PacketPlayOutTabComplete(packetplayintabcomplete.getId(), suggestions));
357 361 });
358 362 }
359 -@@ -792,6 +1025,13 @@
363 +@@ -792,6 +1029,13 @@
360 364
361 365 if (container instanceof ContainerMerchant) {
362 366 ContainerMerchant containermerchant = (ContainerMerchant) container;
363 367 + // CraftBukkit start
364 368 + final org.bukkit.event.inventory.TradeSelectEvent tradeSelectEvent = CraftEventFactory.callTradeSelectEvent(this.player, i, containermerchant);
365 369 + if (tradeSelectEvent.isCancelled()) {
366 370 + this.player.getBukkitEntity().updateInventory();
367 371 + return;
368 372 + }
369 373 + // CraftBukkit end
370 374
371 375 if (!containermerchant.stillValid(this.player)) {
372 376 PlayerConnection.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, containermerchant);
373 -@@ -806,6 +1046,13 @@
377 +@@ -806,6 +1050,13 @@
374 378
375 379 @Override
376 380 public void handleEditBook(PacketPlayInBEdit packetplayinbedit) {
377 381 + // CraftBukkit start
378 382 + if (this.lastBookTick + 20 > MinecraftServer.currentTick) {
379 383 + this.disconnect("Book edited too quickly!");
380 384 + return;
381 385 + }
382 386 + this.lastBookTick = MinecraftServer.currentTick;
383 387 + // CraftBukkit end
384 388 int i = packetplayinbedit.getSlot();
385 389
386 390 if (PlayerInventory.isHotbarSlot(i) || i == 40) {
387 -@@ -814,7 +1061,7 @@
391 +@@ -814,7 +1065,7 @@
388 392
389 393 Objects.requireNonNull(list);
390 394 optional.ifPresent(list::add);
391 395 - Stream stream = packetplayinbedit.getPages().stream().limit(100L);
392 396 + Stream<String> stream = packetplayinbedit.getPages().stream().limit(100L); // CraftBukkit - decompile error
393 397
394 398 Objects.requireNonNull(list);
395 399 stream.forEach(list::add);
396 -@@ -832,7 +1079,7 @@
400 +@@ -832,7 +1083,7 @@
397 401 ItemStack itemstack = this.player.getInventory().getItem(i);
398 402
399 403 if (itemstack.is(Items.WRITABLE_BOOK)) {
400 404 - this.updateBookPages(list, UnaryOperator.identity(), itemstack);
401 405 + this.updateBookPages(list, UnaryOperator.identity(), itemstack.copy(), i, itemstack); // CraftBukkit
402 406 }
403 407 }
404 408
405 -@@ -857,16 +1104,16 @@
409 +@@ -857,16 +1108,16 @@
406 410
407 411 this.updateBookPages(list, (s) -> {
408 412 return IChatBaseComponent.ChatSerializer.toJson(IChatBaseComponent.literal(s));
409 413 - }, itemstack1);
410 414 - this.player.getInventory().setItem(i, itemstack1);
411 415 + }, itemstack1, i, itemstack); // CraftBukkit
412 416 + this.player.getInventory().setItem(i, itemstack); // CraftBukkit - event factory updates the hand book
413 417 }
414 418 }
415 419
416 420 - private void updateBookPages(List<FilteredText> list, UnaryOperator<String> unaryoperator, ItemStack itemstack) {
417 421 + private void updateBookPages(List<FilteredText> list, UnaryOperator<String> unaryoperator, ItemStack itemstack, int slot, ItemStack handItem) { // CraftBukkit
418 422 NBTTagList nbttaglist = new NBTTagList();
419 423
420 424 if (this.player.isTextFilteringEnabled()) {
421 425 - Stream stream = list.stream().map((filteredtext) -> {
422 426 + Stream<NBTTagString> stream = list.stream().map((filteredtext) -> { // CraftBukkit - decompile error
423 427 return NBTTagString.valueOf((String) unaryoperator.apply(filteredtext.filteredOrEmpty()));
424 428 });
425 429
426 -@@ -892,6 +1139,7 @@
430 +@@ -892,6 +1143,7 @@
427 431 }
428 432
429 433 itemstack.addTagElement("pages", nbttaglist);
430 434 + CraftEventFactory.handleEditBookEvent(player, slot, handItem, itemstack); // CraftBukkit
431 435 }
432 436
433 437 @Override
434 -@@ -928,7 +1176,7 @@
438 +@@ -928,7 +1180,7 @@
435 439 } else {
436 440 WorldServer worldserver = this.player.getLevel();
437 441
438 442 - if (!this.player.wonGame) {
439 443 + if (!this.player.wonGame && !this.player.isImmobile()) { // CraftBukkit
440 444 if (this.tickCount == 0) {
441 445 this.resetPosition();
442 446 }
443 -@@ -938,7 +1186,7 @@
447 +@@ -938,7 +1190,7 @@
444 448 this.awaitingTeleportTime = this.tickCount;
445 449 this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
446 450 }
447 451 -
448 452 + this.allowedPlayerTicks = 20; // CraftBukkit
449 453 } else {
450 454 this.awaitingTeleportTime = this.tickCount;
451 455 double d0 = clampHorizontal(packetplayinflying.getX(this.player.getX()));
452 -@@ -950,7 +1198,15 @@
456 +@@ -950,7 +1202,15 @@
453 457 if (this.player.isPassenger()) {
454 458 this.player.absMoveTo(this.player.getX(), this.player.getY(), this.player.getZ(), f, f1);
455 459 this.player.getLevel().getChunkSource().move(this.player);
456 460 + this.allowedPlayerTicks = 20; // CraftBukkit
457 461 } else {
458 462 + // CraftBukkit - Make sure the move is valid but then reset it for plugins to modify
459 463 + double prevX = player.getX();
460 464 + double prevY = player.getY();
461 465 + double prevZ = player.getZ();
462 466 + float prevYaw = player.getYRot();
463 467 + float prevPitch = player.getXRot();
464 468 + // CraftBukkit end
465 469 double d3 = this.player.getX();
466 470 double d4 = this.player.getY();
467 471 double d5 = this.player.getZ();
468 -@@ -970,15 +1226,33 @@
472 +@@ -970,15 +1230,33 @@
469 473 ++this.receivedMovePacketCount;
470 474 int i = this.receivedMovePacketCount - this.knownMovePacketCount;
471 475
472 476 - if (i > 5) {
473 477 + // CraftBukkit start - handle custom speeds and skipped ticks
474 478 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
475 479 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
476 480 + this.lastTick = (int) (System.currentTimeMillis() / 50);
477 481 +
478 482 + if (i > Math.max(this.allowedPlayerTicks, 5)) {
494 498 +
495 499 if (!this.player.isChangingDimension() && (!this.player.getLevel().getGameRules().getBoolean(GameRules.RULE_DISABLE_ELYTRA_MOVEMENT_CHECK) || !this.player.isFallFlying())) {
496 500 float f2 = this.player.isFallFlying() ? 300.0F : 100.0F;
497 501
498 502 - if (d11 - d10 > (double) (f2 * (float) i) && !this.isSingleplayerOwner()) {
499 503 + if (d11 - d10 > Math.max(f2, Math.pow((double) (10.0F * (float) i * speed), 2)) && !this.isSingleplayerOwner()) {
500 504 + // CraftBukkit end
501 505 PlayerConnection.LOGGER.warn("{} moved too quickly! {},{},{}", new Object[]{this.player.getName().getString(), d7, d8, d9});
502 506 this.teleport(this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYRot(), this.player.getXRot());
503 507 return;
504 -@@ -999,6 +1273,7 @@
508 +@@ -999,6 +1277,7 @@
505 509 boolean flag1 = this.player.verticalCollisionBelow;
506 510
507 511 this.player.move(EnumMoveType.PLAYER, new Vec3D(d7, d8, d9));
508 512 + this.player.onGround = packetplayinflying.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
509 513 double d12 = d8;
510 514
511 515 d7 = d0 - this.player.getX();
512 -@@ -1018,8 +1293,71 @@
516 +@@ -1018,8 +1297,71 @@
513 517
514 518 this.player.absMoveTo(d0, d1, d2, f, f1);
515 519 if (!this.player.noPhysics && !this.player.isSleeping() && (flag2 && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew(worldserver, axisalignedbb))) {
516 520 - this.teleport(d3, d4, d5, f, f1);
517 521 + this.internalTeleport(d3, d4, d5, f, f1, Collections.emptySet(), false); // 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.
518 522 } else {
519 523 + // CraftBukkit start - fire PlayerMoveEvent
520 524 + // Rest to old location first
521 525 + this.player.absMoveTo(prevX, prevY, prevZ, prevYaw, prevPitch);
522 526 +
575 579 + return;
576 580 + }
577 581 + }
578 582 + }
579 583 + this.player.absMoveTo(d0, d1, d2, f, f1); // Copied from above
580 584 + // CraftBukkit end
581 585 +
582 586 this.clientIsFloating = d12 >= -0.03125D && !flag1 && this.player.gameMode.getGameModeForPlayer() != EnumGamemode.SPECTATOR && !this.server.isFlightAllowed() && !this.player.getAbilities().mayfly && !this.player.hasEffect(MobEffects.LEVITATION) && !this.player.isFallFlying() && !this.player.isAutoSpinAttack() && this.noBlocksAround(this.player);
583 587 this.player.getLevel().getChunkSource().move(this.player);
584 588 this.player.doCheckFallDamage(this.player.getY() - d6, packetplayinflying.isOnGround());
585 -@@ -1058,19 +1396,80 @@
589 +@@ -1058,19 +1400,80 @@
586 590 return true;
587 591 }
588 592
589 593 + // CraftBukkit start - Delegate to teleport(Location)
590 594 public void dismount(double d0, double d1, double d2, float f, float f1) {
591 595 - this.teleport(d0, d1, d2, f, f1, Collections.emptySet(), true);
592 596 + this.dismount(d0, d1, d2, f, f1, PlayerTeleportEvent.TeleportCause.UNKNOWN);
593 597 + }
594 598 +
595 599 + public void dismount(double d0, double d1, double d2, float f, float f1, PlayerTeleportEvent.TeleportCause cause) {
605 609 + this.teleport(d0, d1, d2, f, f1, Collections.emptySet(), false, cause);
606 610 }
607 611
608 612 public void teleport(double d0, double d1, double d2, float f, float f1, Set<PacketPlayOutPosition.EnumPlayerTeleportFlags> set) {
609 613 - this.teleport(d0, d1, d2, f, f1, set, false);
610 614 + this.teleport(d0, d1, d2, f, f1, set, PlayerTeleportEvent.TeleportCause.UNKNOWN);
611 615 + }
612 616 +
613 617 + public void teleport(double d0, double d1, double d2, float f, float f1, Set<PacketPlayOutPosition.EnumPlayerTeleportFlags> set, PlayerTeleportEvent.TeleportCause cause) {
614 618 + this.teleport(d0, d1, d2, f, f1, set, false, cause);
615 - }
616 -
617 -- public void teleport(double d0, double d1, double d2, float f, float f1, Set<PacketPlayOutPosition.EnumPlayerTeleportFlags> set, boolean flag) {
619 ++ }
620 ++
618 621 + public boolean teleport(double d0, double d1, double d2, float f, float f1, Set<PacketPlayOutPosition.EnumPlayerTeleportFlags> set, boolean flag, PlayerTeleportEvent.TeleportCause cause) { // CraftBukkit - Return event status
619 622 + Player player = this.getCraftPlayer();
620 623 + Location from = player.getLocation();
621 624 +
622 625 + double x = d0;
623 626 + double y = d1;
624 627 + double z = d2;
625 628 + float yaw = f;
626 629 + float pitch = f1;
627 630 +
640 643 + to = event.isCancelled() ? event.getFrom() : event.getTo();
641 644 + d0 = to.getX();
642 645 + d1 = to.getY();
643 646 + d2 = to.getZ();
644 647 + f = to.getYaw();
645 648 + f1 = to.getPitch();
646 649 + }
647 650 +
648 651 + this.internalTeleport(d0, d1, d2, f, f1, set, flag);
649 652 + return event.isCancelled(); // CraftBukkit - Return event status
650 -+ }
651 -+
653 + }
654 +
655 +- public void teleport(double d0, double d1, double d2, float f, float f1, Set<PacketPlayOutPosition.EnumPlayerTeleportFlags> set, boolean flag) {
652 656 + public void teleport(Location dest) {
653 657 + internalTeleport(dest.getX(), dest.getY(), dest.getZ(), dest.getYaw(), dest.getPitch(), Collections.<PacketPlayOutPosition.EnumPlayerTeleportFlags>emptySet(), true);
654 658 + }
655 659 +
656 660 + private void internalTeleport(double d0, double d1, double d2, float f, float f1, Set<PacketPlayOutPosition.EnumPlayerTeleportFlags> set, boolean flag) {
657 661 + // CraftBukkit start
658 662 + if (Float.isNaN(f)) {
659 663 + f = 0;
660 664 + }
661 665 + if (Float.isNaN(f1)) {
662 666 + f1 = 0;
663 667 + }
664 668 +
665 669 + this.justTeleported = true;
666 670 + // CraftBukkit end
667 671 double d3 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.X) ? this.player.getX() : 0.0D;
668 672 double d4 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y) ? this.player.getY() : 0.0D;
669 673 double d5 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Z) ? this.player.getZ() : 0.0D;
670 -@@ -1082,6 +1481,14 @@
674 +@@ -1082,6 +1485,14 @@
671 675 this.awaitingTeleport = 0;
672 676 }
673 677
674 678 + // CraftBukkit start - update last location
675 679 + this.lastPosX = this.awaitingPositionFromClient.x;
676 680 + this.lastPosY = this.awaitingPositionFromClient.y;
677 681 + this.lastPosZ = this.awaitingPositionFromClient.z;
678 682 + this.lastYaw = f;
679 683 + this.lastPitch = f1;
680 684 + // CraftBukkit end
681 685 +
682 686 this.awaitingTeleportTime = this.tickCount;
683 687 this.player.absMoveTo(d0, d1, d2, f, f1);
684 688 this.player.connection.send(new PacketPlayOutPosition(d0 - d3, d1 - d4, d2 - d5, f - f2, f1 - f3, set, this.awaitingTeleport, flag));
685 -@@ -1090,6 +1497,7 @@
689 +@@ -1090,6 +1501,7 @@
686 690 @Override
687 691 public void handlePlayerAction(PacketPlayInBlockDig packetplayinblockdig) {
688 692 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinblockdig, this, this.player.getLevel());
689 693 + if (this.player.isImmobile()) return; // CraftBukkit
690 694 BlockPosition blockposition = packetplayinblockdig.getPos();
691 695
692 696 this.player.resetLastActionTime();
693 -@@ -1100,14 +1508,46 @@
697 +@@ -1100,14 +1512,46 @@
694 698 if (!this.player.isSpectator()) {
695 699 ItemStack itemstack = this.player.getItemInHand(EnumHand.OFF_HAND);
696 700
697 701 - this.player.setItemInHand(EnumHand.OFF_HAND, this.player.getItemInHand(EnumHand.MAIN_HAND));
698 702 - this.player.setItemInHand(EnumHand.MAIN_HAND, itemstack);
699 703 + // CraftBukkit start - inspiration taken from DispenserRegistry (See SpigotCraft#394)
700 704 + CraftItemStack mainHand = CraftItemStack.asCraftMirror(itemstack);
701 705 + CraftItemStack offHand = CraftItemStack.asCraftMirror(this.player.getItemInHand(EnumHand.MAIN_HAND));
702 706 + PlayerSwapHandItemsEvent swapItemsEvent = new PlayerSwapHandItemsEvent(getCraftPlayer(), mainHand.clone(), offHand.clone());
703 707 + this.cserver.getPluginManager().callEvent(swapItemsEvent);
732 736 + if (this.dropCount >= 20) {
733 737 + LOGGER.warn(this.player.getScoreboardName() + " dropped their items too quickly!");
734 738 + this.disconnect("You dropped your items too quickly (Hacking?)");
735 739 + return;
736 740 + }
737 741 + }
738 742 + // CraftBukkit end
739 743 this.player.drop(false);
740 744 }
741 745
742 -@@ -1145,6 +1585,7 @@
746 +@@ -1145,6 +1589,7 @@
743 747 @Override
744 748 public void handleUseItemOn(PacketPlayInUseItem packetplayinuseitem) {
745 749 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinuseitem, this, this.player.getLevel());
746 750 + if (this.player.isImmobile()) return; // CraftBukkit
747 751 this.player.connection.ackBlockChangesUpTo(packetplayinuseitem.getSequence());
748 752 WorldServer worldserver = this.player.getLevel();
749 753 EnumHand enumhand = packetplayinuseitem.getHand();
750 -@@ -1166,6 +1607,7 @@
754 +@@ -1166,6 +1611,7 @@
751 755
752 756 if (blockposition.getY() < i) {
753 757 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)) {
754 758 + this.player.stopUsingItem(); // CraftBukkit - SPIGOT-4706
755 759 EnumInteractionResult enuminteractionresult = this.player.gameMode.useItemOn(this.player, worldserver, itemstack, enumhand, movingobjectpositionblock);
756 760
757 761 if (enumdirection == EnumDirection.UP && !enuminteractionresult.consumesAction() && blockposition.getY() >= i - 1 && wasBlockPlacementAttempt(this.player, itemstack)) {
758 -@@ -1193,6 +1635,7 @@
762 +@@ -1193,6 +1639,7 @@
759 763 @Override
760 764 public void handleUseItem(PacketPlayInBlockPlace packetplayinblockplace) {
761 765 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinblockplace, this, this.player.getLevel());
762 766 + if (this.player.isImmobile()) return; // CraftBukkit
763 767 this.ackBlockChangesUpTo(packetplayinblockplace.getSequence());
764 768 WorldServer worldserver = this.player.getLevel();
765 769 EnumHand enumhand = packetplayinblockplace.getHand();
766 -@@ -1200,6 +1643,44 @@
770 +@@ -1200,6 +1647,44 @@
767 771
768 772 this.player.resetLastActionTime();
769 773 if (!itemstack.isEmpty()) {
770 774 + // CraftBukkit start
771 775 + // Raytrace to look for 'rogue armswings'
772 776 + float f1 = this.player.getXRot();
773 777 + float f2 = this.player.getYRot();
774 778 + double d0 = this.player.getX();
775 779 + double d1 = this.player.getY() + (double) this.player.getEyeHeight();
776 780 + double d2 = this.player.getZ();
801 805 + player.gameMode.firedInteract = false;
802 806 + }
803 807 +
804 808 + if (cancelled) {
805 809 + this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524
806 810 + return;
807 811 + }
808 812 EnumInteractionResult enuminteractionresult = this.player.gameMode.useItem(this.player, worldserver, itemstack, enumhand);
809 813
810 814 if (enuminteractionresult.shouldSwing()) {
811 -@@ -1220,7 +1701,7 @@
815 +@@ -1220,7 +1705,7 @@
812 816 Entity entity = packetplayinspectate.getEntity(worldserver);
813 817
814 818 if (entity != null) {
815 819 - this.player.teleportTo(worldserver, entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot());
816 820 + this.player.teleportTo(worldserver, entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot(), org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.SPECTATE); // CraftBukkit
817 821 return;
818 822 }
819 823 }
820 -@@ -1235,6 +1716,7 @@
824 +@@ -1235,6 +1720,7 @@
821 825 PlayerConnection.LOGGER.info("Disconnecting {} due to resource pack rejection", this.player.getName());
822 826 this.disconnect(IChatBaseComponent.translatable("multiplayer.requiredTexturePrompt.disconnect"));
823 827 }
824 828 + this.cserver.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(getCraftPlayer(), PlayerResourcePackStatusEvent.Status.values()[packetplayinresourcepackstatus.action.ordinal()])); // CraftBukkit
825 829
826 830 }
827 831
828 -@@ -1254,11 +1736,26 @@
832 +@@ -1254,11 +1740,26 @@
829 833
830 834 @Override
831 835 public void onDisconnect(IChatBaseComponent ichatbasecomponent) {
832 836 + // CraftBukkit start - Rarely it would send a disconnect line twice
833 837 + if (this.processedDisconnect) {
834 838 + return;
835 839 + } else {
836 840 + this.processedDisconnect = true;
837 841 + }
838 842 + // CraftBukkit end
846 850 this.player.disconnect();
847 851 - this.server.getPlayerList().remove(this.player);
848 852 + String quitMessage = this.server.getPlayerList().remove(this.player);
849 853 + if ((quitMessage != null) && (quitMessage.length() > 0)) {
850 854 + this.server.getPlayerList().broadcastMessage(CraftChatMessage.fromString(quitMessage));
851 855 + }
852 856 + // CraftBukkit end
853 857 this.player.getTextFilter().leave();
854 858 if (this.isSingleplayerOwner()) {
855 859 PlayerConnection.LOGGER.info("Stopping singleplayer server as player logged out");
856 -@@ -1281,6 +1778,15 @@
860 +@@ -1281,6 +1782,15 @@
857 861 }
858 862
859 863 public void send(Packet<?> packet, @Nullable PacketSendListener packetsendlistener) {
860 864 + // CraftBukkit start
861 865 + if (packet == null) {
862 866 + return;
863 867 + } else if (packet instanceof PacketPlayOutSpawnPosition) {
864 868 + PacketPlayOutSpawnPosition packet6 = (PacketPlayOutSpawnPosition) packet;
865 869 + this.player.compassTarget = new Location(this.getCraftPlayer().getWorld(), packet6.pos.getX(), packet6.pos.getY(), packet6.pos.getZ());
866 870 + }
867 871 + // CraftBukkit end
868 872 +
869 873 try {
870 874 this.connection.send(packet, packetsendlistener);
871 875 } catch (Throwable throwable) {
872 -@@ -1297,7 +1803,16 @@
876 +@@ -1297,7 +1807,16 @@
873 877 @Override
874 878 public void handleSetCarriedItem(PacketPlayInHeldItemSlot packetplayinhelditemslot) {
875 879 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinhelditemslot, this, this.player.getLevel());
876 880 + if (this.player.isImmobile()) return; // CraftBukkit
877 881 if (packetplayinhelditemslot.getSlot() >= 0 && packetplayinhelditemslot.getSlot() < PlayerInventory.getSelectionSize()) {
878 882 + PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getCraftPlayer(), this.player.getInventory().selected, packetplayinhelditemslot.getSlot());
879 883 + this.cserver.getPluginManager().callEvent(event);
880 884 + if (event.isCancelled()) {
881 885 + this.send(new PacketPlayOutHeldItemSlot(this.player.getInventory().selected));
882 886 + this.player.resetLastActionTime();
883 887 + return;
884 888 + }
885 889 + // CraftBukkit end
886 890 if (this.player.getInventory().selected != packetplayinhelditemslot.getSlot() && this.player.getUsedItemHand() == EnumHand.MAIN_HAND) {
887 891 this.player.stopUsingItem();
888 892 }
889 -@@ -1306,16 +1821,23 @@
893 +@@ -1306,16 +1825,23 @@
890 894 this.player.resetLastActionTime();
891 895 } else {
892 896 PlayerConnection.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString());
893 897 + this.disconnect("Invalid hotbar selection (Hacking?)"); // CraftBukkit
894 898 }
895 899 }
896 900
897 901 @Override
898 902 public void handleChat(PacketPlayInChat packetplayinchat) {
899 903 + // CraftBukkit start - async chat
904 908 + // CraftBukkit end
905 909 if (isChatMessageIllegal(packetplayinchat.message())) {
906 910 this.disconnect(IChatBaseComponent.translatable("multiplayer.disconnect.illegal_characters"));
907 911 } else {
908 912 if (this.tryHandleChat(packetplayinchat.message(), packetplayinchat.timeStamp(), packetplayinchat.lastSeenMessages())) {
909 913 - this.server.submit(() -> {
910 914 + // this.server.submit(() -> { // CraftBukkit - async chat
911 915 PlayerChatMessage playerchatmessage = this.getSignedMessage(packetplayinchat);
912 916
913 917 if (this.verifyChatMessage(playerchatmessage)) {
914 -@@ -1328,10 +1850,10 @@
918 +@@ -1328,10 +1854,10 @@
915 919 PlayerChatMessage playerchatmessage1 = ((PlayerChatMessage) completablefuture1.join()).filter(filtermask);
916 920
917 921 this.broadcastChatMessage(playerchatmessage1);
918 922 - }, this.server);
919 923 + }, this.server.chatExecutor); // CraftBukkit - async chat
920 924 });
921 925 }
922 926 - });
923 927 + // }); // CraftBukkit - async chat
924 928 }
925 929
926 930 }
927 -@@ -1353,8 +1875,21 @@
931 +@@ -1353,8 +1879,21 @@
928 932 }
929 933
930 934 private void performChatCommand(ServerboundChatCommandPacket serverboundchatcommandpacket) {
931 935 - ParseResults<CommandListenerWrapper> parseresults = this.parseCommand(serverboundchatcommandpacket.command());
932 936 - Map<String, PlayerChatMessage> map = this.collectSignedArguments(serverboundchatcommandpacket, PreviewableCommand.of(parseresults));
933 937 + // CraftBukkit start
934 938 + String command = "/" + serverboundchatcommandpacket.command();
935 939 + PlayerConnection.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + command);
936 940 +
937 941 + PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(getCraftPlayer(), command, new LazyPlayerSet(server));
941 945 + return;
942 946 + }
943 947 + command = event.getMessage().substring(1);
944 948 +
945 949 + ParseResults<CommandListenerWrapper> parseresults = this.parseCommand(command);
946 950 + Map<String, PlayerChatMessage> map = (serverboundchatcommandpacket.command().equals(command)) ? this.collectSignedArguments(serverboundchatcommandpacket, PreviewableCommand.of(parseresults)) : Collections.emptyMap();
947 951 + // CraftBukkit end
948 952 Iterator iterator = map.values().iterator();
949 953
950 954 PlayerChatMessage playerchatmessage;
951 -@@ -1366,7 +1901,7 @@
955 +@@ -1366,7 +1905,7 @@
952 956 parseresults = CommandDispatcher.mapSource(parseresults, (commandlistenerwrapper) -> {
953 957 return commandlistenerwrapper.withSigningContext(commandsigningcontext_a);
954 958 });
955 959 - this.server.getCommands().performCommand(parseresults, serverboundchatcommandpacket.command());
956 960 + this.server.getCommands().performCommand(parseresults, command); // CraftBukkit
957 961 return;
958 962 }
959 963
960 -@@ -1415,7 +1950,7 @@
964 +@@ -1415,7 +1954,7 @@
961 965 PlayerConnection.LOGGER.warn("{} sent out-of-order chat: '{}'", this.player.getName().getString(), s);
962 966 this.disconnect(IChatBaseComponent.translatable("multiplayer.disconnect.out_of_order_chat"));
963 967 return false;
964 968 - } else if (this.player.getChatVisibility() == EnumChatVisibility.HIDDEN) {
965 969 + } else if (this.player.isRemoved() || this.player.getChatVisibility() == EnumChatVisibility.HIDDEN) { // CraftBukkit - dead men tell no tales
966 970 this.send(new ClientboundSystemChatPacket(IChatBaseComponent.translatable("chat.disabled.options").withStyle(EnumChatFormat.RED), false));
967 971 return false;
968 972 } else {
969 -@@ -1459,6 +1994,135 @@
973 +@@ -1459,6 +1998,135 @@
970 974 return false;
971 975 }
972 976
973 977 + // CraftBukkit start - add method
974 978 + public void chat(String s, PlayerChatMessage original, boolean async) {
975 979 + if (s.isEmpty() || this.player.getChatVisibility() == EnumChatVisibility.HIDDEN) {
976 980 + return;
977 981 + }
978 982 + OutgoingPlayerChatMessage outgoing = OutgoingPlayerChatMessage.create(original);
979 983 +
1095 1099 + player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command");
1096 1100 + java.util.logging.Logger.getLogger(PlayerConnection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
1097 1101 + return;
1098 1102 + }
1099 1103 + }
1100 1104 + // CraftBukkit end
1101 1105 +
1102 1106 private PlayerChatMessage getSignedMessage(PacketPlayInChat packetplayinchat) {
1103 1107 MessageSigner messagesigner = packetplayinchat.getSigner(this.player);
1104 1108 SignedMessageChain.c signedmessagechain_c = new SignedMessageChain.c(packetplayinchat.signature());
1105 -@@ -1475,7 +2139,25 @@
1109 +@@ -1475,7 +2143,25 @@
1106 1110 }
1107 1111
1108 1112 private void broadcastChatMessage(PlayerChatMessage playerchatmessage) {
1109 1113 - this.server.getPlayerList().broadcastChatMessage(playerchatmessage, this.player, ChatMessageType.bind(ChatMessageType.CHAT, (Entity) this.player));
1110 1114 + // CraftBukkit start
1111 1115 + String s = playerchatmessage.signedContent().plain();
1112 1116 + if (s.isEmpty()) {
1113 1117 + LOGGER.warn(this.player.getScoreboardName() + " tried to send an empty message");
1114 1118 + } else if (getCraftPlayer().isConversing()) {
1115 1119 + final String conversationInput = s;
1122 1126 + } else if (this.player.getChatVisibility() == EnumChatVisibility.SYSTEM) { // Re-add "Command Only" flag check
1123 1127 + this.send(new ClientboundSystemChatPacket(IChatBaseComponent.translatable("chat.cannotSend").withStyle(EnumChatFormat.RED), false));
1124 1128 + } else {
1125 1129 + this.chat(s, playerchatmessage, true);
1126 1130 + }
1127 1131 + // this.server.getPlayerList().broadcastChatMessage(playerchatmessage, this.player, ChatMessageType.bind(ChatMessageType.CHAT, (Entity) this.player));
1128 1132 + // CraftBukkit end
1129 1133 this.detectRateSpam();
1130 1134 }
1131 1135
1132 -@@ -1503,8 +2185,10 @@
1136 +@@ -1503,8 +2189,10 @@
1133 1137 }
1134 1138
1135 1139 private void detectRateSpam() {
1136 1140 - this.chatSpamTickCount += 20;
1137 1141 - if (this.chatSpamTickCount > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
1138 1142 + // CraftBukkit start - replaced with thread safe throttle
1139 1143 + // this.chatSpamTickCount += 20;
1140 1144 + if (this.chatSpamTickCount.addAndGet(20) > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
1141 1145 + // CraftBukkit end
1142 1146 this.disconnect(IChatBaseComponent.translatable("disconnect.spam"));
1143 1147 }
1144 1148
1145 -@@ -1568,7 +2252,7 @@
1149 +@@ -1568,7 +2256,7 @@
1146 1150 List<PreviewableCommand.a<CommandListenerWrapper>> list = previewablecommand.arguments();
1147 1151
1148 1152 if (list.isEmpty()) {
1149 1153 - return CompletableFuture.completedFuture((Object) null);
1150 1154 + return CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
1151 1155 } else {
1152 1156 for (int i = list.size() - 1; i >= 0; --i) {
1153 1157 PreviewableCommand.a previewablecommand_a = (PreviewableCommand.a) list.get(i);
1154 -@@ -1580,11 +2264,11 @@
1158 +@@ -1580,11 +2268,11 @@
1155 1159 return completablefuture;
1156 1160 }
1157 1161 } catch (CommandSyntaxException commandsyntaxexception) {
1158 1162 - return CompletableFuture.completedFuture((Object) null);
1159 1163 + return CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
1160 1164 }
1161 1165 }
1162 1166
1163 1167 - return CompletableFuture.completedFuture((Object) null);
1164 1168 + return CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
1165 1169 }
1166 1170 }
1167 1171
1168 -@@ -1611,13 +2295,65 @@
1172 +@@ -1611,13 +2299,65 @@
1169 1173 @Override
1170 1174 public void handleAnimate(PacketPlayInArmAnimation packetplayinarmanimation) {
1171 1175 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinarmanimation, this, this.player.getLevel());
1172 1176 + if (this.player.isImmobile()) return; // CraftBukkit
1173 1177 this.player.resetLastActionTime();
1174 1178 + // CraftBukkit start - Raytrace to look for 'rogue armswings'
1175 1179 + float f1 = this.player.getXRot();
1176 1180 + float f2 = this.player.getYRot();
1177 1181 + double d0 = this.player.getX();
1178 1182 + double d1 = this.player.getY() + (double) this.player.getEyeHeight();
1224 1228 +
1225 1229 + if (e2.isCancelled()) {
1226 1230 + return;
1227 1231 + }
1228 1232 + break;
1229 1233 + }
1230 1234 + // CraftBukkit end
1231 1235 this.player.resetLastActionTime();
1232 1236 IJumpable ijumpable;
1233 1237
1234 -@@ -1702,6 +2438,7 @@
1238 +@@ -1702,6 +2442,7 @@
1235 1239 @Override
1236 1240 public void handleInteract(PacketPlayInUseEntity packetplayinuseentity) {
1237 1241 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinuseentity, this, this.player.getLevel());
1238 1242 + if (this.player.isImmobile()) return; // CraftBukkit
1239 1243 WorldServer worldserver = this.player.getLevel();
1240 1244 final Entity entity = packetplayinuseentity.getTarget(worldserver);
1241 1245
1242 -@@ -1714,10 +2451,44 @@
1246 +@@ -1714,10 +2455,49 @@
1243 1247
1244 1248 if (entity.distanceToSqr(this.player.getEyePosition()) < PlayerConnection.MAX_INTERACTION_DISTANCE) {
1245 1249 packetplayinuseentity.dispatch(new PacketPlayInUseEntity.c() {
1246 1250 - private void performInteraction(EnumHand enumhand, PlayerConnection.a playerconnection_a) {
1247 1251 + private void performInteraction(EnumHand enumhand, PlayerConnection.a playerconnection_a, PlayerInteractEntityEvent event) { // CraftBukkit
1248 1252 ItemStack itemstack = PlayerConnection.this.player.getItemInHand(enumhand).copy();
1249 1253 + // CraftBukkit start
1250 1254 + ItemStack itemInHand = PlayerConnection.this.player.getItemInHand(enumhand);
1251 1255 + boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEAD && entity instanceof EntityInsentient;
1252 1256 + Item origItem = player.getInventory().getSelected() == null ? null : player.getInventory().getSelected().getItem();
1260 1264 + }
1261 1265 +
1262 1266 + if (triggerLeashUpdate && (event.isCancelled() || player.getInventory().getSelected() == null || player.getInventory().getSelected().getItem() != origItem)) {
1263 1267 + // Refresh the current leash state
1264 1268 + send(new PacketPlayOutAttachEntity(entity, ((EntityInsentient) entity).getLeashHolder()));
1265 1269 + }
1266 1270 +
1267 1271 + if (event.isCancelled() || player.getInventory().getSelected() == null || player.getInventory().getSelected().getItem() != origItem) {
1268 1272 + // Refresh the current entity metadata
1269 1273 + send(new PacketPlayOutEntityMetadata(entity.getId(), entity.getEntityData(), true));
1274 ++ // SPIGOT-7136 - Allays
1275 ++ if (entity instanceof Allay) {
1276 ++ send(new PacketPlayOutEntityEquipment(entity.getId(), Arrays.stream(EnumItemSlot.values()).map((slot) -> Pair.of(slot, ((EntityLiving) entity).getItemBySlot(slot).copy())).collect(Collectors.toList())));
1277 ++ player.containerMenu.sendAllDataToRemote();
1278 ++ }
1270 1279 + }
1271 1280 +
1272 1281 + if (event.isCancelled()) {
1273 1282 + return;
1274 1283 + }
1275 1284 + // CraftBukkit end
1276 1285 +
1277 1286 EnumInteractionResult enuminteractionresult = playerconnection_a.run(PlayerConnection.this.player, entity, enumhand);
1278 1287
1279 1288 + // CraftBukkit start
1280 1289 + if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) {
1281 -+ player.containerMenu.sendAllDataToRemote();
1290 ++ player.containerMenu.sendAllDataToRemote();
1282 1291 + }
1283 1292 + // CraftBukkit end
1284 1293 +
1285 1294 if (enuminteractionresult.consumesAction()) {
1286 1295 CriterionTriggers.PLAYER_INTERACTED_WITH_ENTITY.trigger(PlayerConnection.this.player, itemstack, entity);
1287 1296 if (enuminteractionresult.shouldSwing()) {
1288 -@@ -1729,20 +2500,27 @@
1297 +@@ -1729,20 +2509,27 @@
1289 1298
1290 1299 @Override
1291 1300 public void onInteraction(EnumHand enumhand) {
1292 1301 - this.performInteraction(enumhand, EntityHuman::interactOn);
1293 1302 + this.performInteraction(enumhand, EntityHuman::interactOn, new PlayerInteractEntityEvent(getCraftPlayer(), entity.getBukkitEntity(), (enumhand == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND));
1294 1303 }
1295 1304
1296 1305 @Override
1297 1306 public void onInteraction(EnumHand enumhand, Vec3D vec3d) {
1298 1307 this.performInteraction(enumhand, (entityplayer, entity1, enumhand1) -> {
1309 1318 + ItemStack itemInHand = PlayerConnection.this.player.getMainHandItem();
1310 1319 PlayerConnection.this.player.attack(entity);
1311 1320 +
1312 1321 + if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) {
1313 1322 + player.containerMenu.sendAllDataToRemote();
1314 1323 + }
1315 1324 + // CraftBukkit end
1316 1325 } else {
1317 1326 PlayerConnection.this.disconnect(IChatBaseComponent.translatable("multiplayer.disconnect.invalid_entity_attacked"));
1318 1327 PlayerConnection.LOGGER.warn("Player {} tried to attack an invalid entity", PlayerConnection.this.player.getName().getString());
1319 -@@ -1787,15 +2565,21 @@
1328 +@@ -1787,15 +2574,21 @@
1320 1329 @Override
1321 1330 public void handleContainerClose(PacketPlayInCloseWindow packetplayinclosewindow) {
1322 1331 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinclosewindow, this, this.player.getLevel());
1323 1332 +
1324 1333 + if (this.player.isImmobile()) return; // CraftBukkit
1325 1334 + CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit
1326 1335 +
1327 1336 this.player.doCloseContainer();
1328 1337 }
1329 1338
1333 1342 + if (this.player.isImmobile()) return; // CraftBukkit
1334 1343 this.player.resetLastActionTime();
1335 1344 - if (this.player.containerMenu.containerId == packetplayinwindowclick.getContainerId()) {
1336 1345 - if (this.player.isSpectator()) {
1337 1346 + if (this.player.containerMenu.containerId == packetplayinwindowclick.getContainerId() && this.player.containerMenu.stillValid(this.player)) { // CraftBukkit
1338 1347 + boolean cancelled = this.player.isSpectator(); // CraftBukkit - see below if
1339 1348 + if (false/*this.player.isSpectator()*/) { // CraftBukkit
1340 1349 this.player.containerMenu.sendAllDataToRemote();
1341 1350 } else if (!this.player.containerMenu.stillValid(this.player)) {
1342 1351 PlayerConnection.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu);
1343 -@@ -1808,7 +2592,284 @@
1352 +@@ -1808,7 +2601,284 @@
1344 1353 boolean flag = packetplayinwindowclick.getStateId() != this.player.containerMenu.getStateId();
1345 1354
1346 1355 this.player.containerMenu.suppressRemoteUpdates();
1347 1356 - this.player.containerMenu.clicked(i, packetplayinwindowclick.getButtonNum(), packetplayinwindowclick.getClickType(), this.player);
1348 1357 + // CraftBukkit start - Call InventoryClickEvent
1349 1358 + if (packetplayinwindowclick.getSlotNum() < -1 && packetplayinwindowclick.getSlotNum() != -999) {
1350 1359 + return;
1351 1360 + }
1352 1361 +
1353 1362 + InventoryView inventory = this.player.containerMenu.getBukkitView();
1619 1628 + if (event instanceof CraftItemEvent || event instanceof SmithItemEvent) {
1620 1629 + // Need to update the inventory on crafting to
1621 1630 + // correctly support custom recipes
1622 1631 + player.containerMenu.sendAllDataToRemote();
1623 1632 + }
1624 1633 + }
1625 1634 + // CraftBukkit end
1626 1635 ObjectIterator objectiterator = Int2ObjectMaps.fastIterable(packetplayinwindowclick.getChangedSlots()).iterator();
1627 1636
1628 1637 while (objectiterator.hasNext()) {
1629 -@@ -1848,6 +2909,7 @@
1638 +@@ -1848,6 +2918,7 @@
1630 1639 @Override
1631 1640 public void handleContainerButtonClick(PacketPlayInEnchantItem packetplayinenchantitem) {
1632 1641 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinenchantitem, this, this.player.getLevel());
1633 1642 + if (this.player.isImmobile()) return; // CraftBukkit
1634 1643 this.player.resetLastActionTime();
1635 1644 if (this.player.containerMenu.containerId == packetplayinenchantitem.getContainerId() && !this.player.isSpectator()) {
1636 1645 if (!this.player.containerMenu.stillValid(this.player)) {
1637 -@@ -1885,6 +2947,43 @@
1646 +@@ -1885,6 +2956,43 @@
1638 1647
1639 1648 boolean flag1 = packetplayinsetcreativeslot.getSlotNum() >= 1 && packetplayinsetcreativeslot.getSlotNum() <= 45;
1640 1649 boolean flag2 = itemstack.isEmpty() || itemstack.getDamageValue() >= 0 && itemstack.getCount() <= 64 && !itemstack.isEmpty();
1641 1650 + if (flag || (flag1 && !ItemStack.matches(this.player.inventoryMenu.getSlot(packetplayinsetcreativeslot.getSlotNum()).getItem(), packetplayinsetcreativeslot.getItem()))) { // Insist on valid slot
1642 1651 + // CraftBukkit start - Call click event
1643 1652 + InventoryView inventory = this.player.inventoryMenu.getBukkitView();
1644 1653 + org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packetplayinsetcreativeslot.getItem());
1645 1654 +
1646 1655 + SlotType type = SlotType.QUICKBAR;
1647 1656 + if (flag) {
1671 1680 + this.player.connection.send(new PacketPlayOutSetSlot(this.player.inventoryMenu.containerId, this.player.inventoryMenu.incrementStateId(), packetplayinsetcreativeslot.getSlotNum(), this.player.inventoryMenu.getSlot(packetplayinsetcreativeslot.getSlotNum()).getItem()));
1672 1681 + this.player.connection.send(new PacketPlayOutSetSlot(-1, this.player.inventoryMenu.incrementStateId(), -1, ItemStack.EMPTY));
1673 1682 + }
1674 1683 + return;
1675 1684 + }
1676 1685 + }
1677 1686 + // CraftBukkit end
1678 1687
1679 1688 if (flag1 && flag2) {
1680 1689 this.player.inventoryMenu.getSlot(packetplayinsetcreativeslot.getSlotNum()).set(itemstack);
1681 -@@ -1907,6 +3006,7 @@
1690 +@@ -1907,6 +3015,7 @@
1682 1691 }
1683 1692
1684 1693 private void updateSignText(PacketPlayInUpdateSign packetplayinupdatesign, List<FilteredText> list) {
1685 1694 + if (this.player.isImmobile()) return; // CraftBukkit
1686 1695 this.player.resetLastActionTime();
1687 1696 WorldServer worldserver = this.player.getLevel();
1688 1697 BlockPosition blockposition = packetplayinupdatesign.getPos();
1689 -@@ -1923,18 +3023,37 @@
1698 +@@ -1923,18 +3032,37 @@
1690 1699
1691 1700 if (!tileentitysign.isEditable() || !this.player.getUUID().equals(tileentitysign.getPlayerWhoMayEdit())) {
1692 1701 PlayerConnection.LOGGER.warn("Player {} just tried to change non-editable sign", this.player.getName().getString());
1693 1702 + this.send(tileentity.getUpdatePacket()); // CraftBukkit
1694 1703 return;
1695 1704 }
1696 1705
1697 1706 + // CraftBukkit start
1698 1707 + Player player = this.player.getBukkitEntity();
1699 1708 + int x = packetplayinupdatesign.getPos().getX();
1719 1728 + IChatBaseComponent[] components = org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.getLines());
1720 1729 + for (int i = 0; i < components.length; i++) {
1721 1730 + tileentitysign.setMessage(i, components[i]);
1722 1731 }
1723 1732 + tileentitysign.isEditable = false;
1724 1733 }
1725 1734 + // CraftBukkit end
1726 1735
1727 1736 tileentitysign.setChanged();
1728 1737 worldserver.sendBlockUpdated(blockposition, iblockdata, iblockdata, 3);
1729 -@@ -1944,6 +3063,7 @@
1738 +@@ -1944,6 +3072,7 @@
1730 1739
1731 1740 @Override
1732 1741 public void handleKeepAlive(PacketPlayInKeepAlive packetplayinkeepalive) {
1733 1742 + PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinkeepalive, this, this.player.getLevel()); // CraftBukkit
1734 1743 if (this.keepAlivePending && packetplayinkeepalive.getId() == this.keepAliveChallenge) {
1735 1744 int i = (int) (SystemUtils.getMillis() - this.keepAliveTime);
1736 1745
1737 -@@ -1958,7 +3078,17 @@
1746 +@@ -1958,7 +3087,17 @@
1738 1747 @Override
1739 1748 public void handlePlayerAbilities(PacketPlayInAbilities packetplayinabilities) {
1740 1749 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinabilities, this, this.player.getLevel());
1741 1750 - this.player.getAbilities().flying = packetplayinabilities.isFlying() && this.player.getAbilities().mayfly;
1742 1751 + // CraftBukkit start
1743 1752 + if (this.player.getAbilities().mayfly && this.player.getAbilities().flying != packetplayinabilities.isFlying()) {
1744 1753 + PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.player.getBukkitEntity(), packetplayinabilities.isFlying());
1745 1754 + this.cserver.getPluginManager().callEvent(event);
1746 1755 + if (!event.isCancelled()) {
1747 1756 + this.player.getAbilities().flying = packetplayinabilities.isFlying(); // Actually set the player's flying status
1748 1757 + } else {
1749 1758 + this.player.onUpdateAbilities(); // Tell the player their ability was reverted
1750 1759 + }
1751 1760 + }
1752 1761 + // CraftBukkit end
1753 1762 }
1754 1763
1755 1764 @Override
1756 -@@ -1967,8 +3097,50 @@
1765 +@@ -1967,8 +3106,50 @@
1757 1766 this.player.updateOptions(packetplayinsettings);
1758 1767 }
1759 1768
1760 1769 - @Override
1761 1770 - public void handleCustomPayload(PacketPlayInCustomPayload packetplayincustompayload) {}
1762 1771 + // CraftBukkit start
1763 1772 + private static final MinecraftKey CUSTOM_REGISTER = new MinecraftKey("register");
1764 1773 + private static final MinecraftKey CUSTOM_UNREGISTER = new MinecraftKey("unregister");
1765 1774 +
1766 1775 + @Override

Everything looks good. We'll let you know here if there's anything you should know about.

Add shortcut