Commits

Collin authored and md_5 committed 01cae71b7a4
SPIGOT-7429: Fix LEFT_CLICK_AIR not working for passable entities and spectators
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 -@@ -185,6 +185,69 @@
3 +@@ -185,6 +185,70 @@
4 4 import net.minecraft.world.phys.shapes.VoxelShapes;
5 5 import org.slf4j.Logger;
6 6
7 7 +// CraftBukkit start
8 8 +import com.mojang.datafixers.util.Pair;
9 9 +import java.util.Arrays;
10 10 +import java.util.concurrent.ExecutionException;
11 11 +import java.util.concurrent.atomic.AtomicInteger;
12 12 +import net.minecraft.network.chat.OutgoingChatMessage;
13 13 +import net.minecraft.network.protocol.game.PacketPlayOutAttachEntity;
18 18 +import net.minecraft.world.entity.animal.Bucketable;
19 19 +import net.minecraft.world.entity.EntityLiving;
20 20 +import net.minecraft.world.entity.EnumItemSlot;
21 21 +import net.minecraft.world.entity.animal.allay.Allay;
22 22 +import net.minecraft.world.inventory.InventoryClickType;
23 23 +import net.minecraft.world.inventory.Slot;
24 24 +import net.minecraft.world.item.crafting.IRecipe;
25 25 +import net.minecraft.world.level.RayTrace;
26 26 +import net.minecraft.world.phys.MovingObjectPosition;
27 27 +import org.bukkit.Location;
28 ++import org.bukkit.craftbukkit.entity.CraftEntity;
28 29 +import org.bukkit.craftbukkit.entity.CraftPlayer;
29 30 +import org.bukkit.craftbukkit.event.CraftEventFactory;
30 31 +import org.bukkit.craftbukkit.inventory.CraftItemStack;
31 32 +import org.bukkit.craftbukkit.util.CraftChatMessage;
32 33 +import org.bukkit.craftbukkit.util.CraftLocation;
33 34 +import org.bukkit.craftbukkit.util.CraftMagicNumbers;
34 35 +import org.bukkit.craftbukkit.util.LazyPlayerSet;
35 36 +import org.bukkit.craftbukkit.util.Waitable;
36 37 +import org.bukkit.entity.Player;
37 38 +import org.bukkit.event.Event;
63 64 +import org.bukkit.event.player.PlayerToggleSprintEvent;
64 65 +import org.bukkit.inventory.CraftingInventory;
65 66 +import org.bukkit.inventory.EquipmentSlot;
66 67 +import org.bukkit.inventory.InventoryView;
67 68 +import org.bukkit.inventory.SmithingInventory;
68 69 +// CraftBukkit end
69 70 +
70 71 public class PlayerConnection implements ServerPlayerConnection, TickablePacketListener, PacketListenerPlayIn {
71 72
72 73 static final Logger LOGGER = LogUtils.getLogger();
73 -@@ -201,7 +264,9 @@
74 +@@ -201,7 +265,9 @@
74 75 private long keepAliveTime;
75 76 private boolean keepAlivePending;
76 77 private long keepAliveChallenge;
77 78 - private int chatSpamTickCount;
78 79 + // CraftBukkit start - multithreaded fields
79 80 + private final AtomicInteger chatSpamTickCount = new AtomicInteger();
80 81 + // CraftBukkit end
81 82 private int dropSpamTickCount;
82 83 private double firstGoodX;
83 84 private double firstGoodY;
84 -@@ -247,8 +312,31 @@
85 +@@ -247,8 +313,31 @@
85 86 this.keepAliveTime = SystemUtils.getMillis();
86 87 entityplayer.getTextFilter().join();
87 88 this.signedMessageDecoder = minecraftserver.enforceSecureProfile() ? SignedMessageChain.b.REJECT_ALL : SignedMessageChain.b.unsigned(entityplayer.getUUID());
88 89 - this.chatMessageChain = new FutureChain(minecraftserver);
89 90 + this.chatMessageChain = new FutureChain(minecraftserver.chatExecutor); // CraftBukkit - async chat
90 91 + // CraftBukkit start - add fields and methods
91 92 + this.cserver = minecraftserver.server;
92 93 + }
93 94 +
94 95 + private final org.bukkit.craftbukkit.CraftServer cserver;
107 108 + private float lastYaw = Float.MAX_VALUE;
108 109 + private boolean justTeleported = false;
109 110 +
110 111 + public CraftPlayer getCraftPlayer() {
111 112 + return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity();
112 113 }
113 114 + // CraftBukkit end
114 115
115 116 @Override
116 117 public void tick() {
117 -@@ -303,7 +391,7 @@
118 +@@ -303,7 +392,7 @@
118 119 this.server.getProfiler().push("keepAlive");
119 120 long i = SystemUtils.getMillis();
120 121
121 122 - if (i - this.keepAliveTime >= 15000L) {
122 123 + if (i - this.keepAliveTime >= 25000L) { // CraftBukkit
123 124 if (this.keepAlivePending) {
124 125 this.disconnect(IChatBaseComponent.translatable("disconnect.timeout"));
125 126 } else {
126 -@@ -315,15 +403,21 @@
127 +@@ -315,15 +404,21 @@
127 128 }
128 129
129 130 this.server.getProfiler().pop();
130 131 + // CraftBukkit start
131 132 + for (int spam; (spam = this.chatSpamTickCount.get()) > 0 && !chatSpamTickCount.compareAndSet(spam, spam - 1); ) ;
132 133 + /* Use thread-safe field access instead
133 134 if (this.chatSpamTickCount > 0) {
134 135 --this.chatSpamTickCount;
135 136 }
136 137 + */
137 138 + // CraftBukkit end
138 139
139 140 if (this.dropSpamTickCount > 0) {
140 141 --this.dropSpamTickCount;
141 142 }
142 143
143 144 if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && SystemUtils.getMillis() - this.player.getLastActionTime() > (long) this.server.getPlayerIdleTimeout() * 1000L * 60L) {
144 145 + this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854
145 146 this.disconnect(IChatBaseComponent.translatable("multiplayer.disconnect.idling"));
146 147 }
147 148
148 -@@ -347,16 +441,67 @@
149 +@@ -347,16 +442,67 @@
149 150 return this.server.isSingleplayerOwner(this.player.getGameProfile());
150 151 }
151 152
152 153 + // CraftBukkit start
153 154 + @Deprecated
154 155 public void disconnect(IChatBaseComponent ichatbasecomponent) {
155 156 + disconnect(CraftChatMessage.fromComponent(ichatbasecomponent));
156 157 + }
157 158 + // CraftBukkit end
158 159 +
207 208 MinecraftServer minecraftserver = this.server;
208 209 NetworkManager networkmanager = this.connection;
209 210
210 211 Objects.requireNonNull(this.connection);
211 212 - minecraftserver.executeBlocking(networkmanager::handleDisconnection);
212 213 + // CraftBukkit - Don't wait
213 214 + minecraftserver.wrapRunnable(networkmanager::handleDisconnection);
214 215 }
215 216
216 217 private <T, R> CompletableFuture<R> filterTextPacket(T t0, BiFunction<ITextFilter, T, CompletableFuture<R>> bifunction) {
217 -@@ -420,7 +565,34 @@
218 +@@ -420,7 +566,34 @@
218 219 double d9 = entity.getDeltaMovement().lengthSqr();
219 220 double d10 = d6 * d6 + d7 * d7 + d8 * d8;
220 221
221 222 - if (d10 - d9 > 100.0D && !this.isSingleplayerOwner()) {
222 223 +
223 224 + // CraftBukkit start - handle custom speeds and skipped ticks
224 225 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
225 226 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
226 227 + this.lastTick = (int) (System.currentTimeMillis() / 50);
227 228 +
243 244 + } else {
244 245 + speed = player.getAbilities().walkingSpeed * 10f;
245 246 + }
246 247 + speed *= 2f; // TODO: Get the speed of the vehicle instead of the player
247 248 +
248 249 + if (d10 - d9 > Math.max(100.0D, Math.pow((double) (10.0F * (float) i * speed), 2)) && !this.isSingleplayerOwner()) {
249 250 + // CraftBukkit end
250 251 PlayerConnection.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", new Object[]{entity.getName().getString(), this.player.getName().getString(), d6, d7, d8});
251 252 this.connection.send(new PacketPlayOutVehicleMove(entity));
252 253 return;
253 -@@ -460,14 +632,72 @@
254 +@@ -460,14 +633,72 @@
254 255 }
255 256
256 257 entity.absMoveTo(d3, d4, d5, f, f1);
257 258 + player.absMoveTo(d3, d4, d5, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
258 259 boolean flag3 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
259 260
260 261 if (flag && (flag2 || !flag3)) {
261 262 entity.absMoveTo(d0, d1, d2, f, f1);
262 263 + player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
263 264 this.connection.send(new PacketPlayOutVehicleMove(entity));
316 317 + this.justTeleported = false;
317 318 + return;
318 319 + }
319 320 + }
320 321 + }
321 322 + // CraftBukkit end
322 323 +
323 324 this.player.serverLevel().getChunkSource().move(this.player);
324 325 this.player.checkMovementStatistics(this.player.getX() - d0, this.player.getY() - d1, this.player.getZ() - d2);
325 326 this.clientVehicleIsFloating = d11 >= -0.03125D && !flag1 && !this.server.isFlightAllowed() && !entity.isNoGravity() && this.noBlocksAround(entity);
326 -@@ -501,6 +731,7 @@
327 +@@ -501,6 +732,7 @@
327 328 }
328 329
329 330 this.awaitingPositionFromClient = null;
330 331 + this.player.serverLevel().getChunkSource().move(this.player); // CraftBukkit
331 332 }
332 333
333 334 }
334 -@@ -508,7 +739,7 @@
335 +@@ -508,7 +740,7 @@
335 336 @Override
336 337 public void handleRecipeBookSeenRecipePacket(PacketPlayInRecipeDisplayed packetplayinrecipedisplayed) {
337 338 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinrecipedisplayed, this, this.player.serverLevel());
338 339 - Optional optional = this.server.getRecipeManager().byKey(packetplayinrecipedisplayed.getRecipe());
339 340 + Optional<? extends IRecipe<?>> optional = this.server.getRecipeManager().byKey(packetplayinrecipedisplayed.getRecipe()); // CraftBukkit - decompile error
340 341 RecipeBookServer recipebookserver = this.player.getRecipeBook();
341 342
342 343 Objects.requireNonNull(recipebookserver);
343 -@@ -538,6 +769,12 @@
344 +@@ -538,6 +770,12 @@
344 345 @Override
345 346 public void handleCustomCommandSuggestions(PacketPlayInTabComplete packetplayintabcomplete) {
346 347 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayintabcomplete, this, this.player.serverLevel());
347 348 + // CraftBukkit start
348 349 + if (chatSpamTickCount.addAndGet(1) > 500 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
349 350 + this.disconnect(IChatBaseComponent.translatable("disconnect.spam"));
350 351 + return;
351 352 + }
352 353 + // CraftBukkit end
353 354 StringReader stringreader = new StringReader(packetplayintabcomplete.getCommand());
354 355
355 356 if (stringreader.canRead() && stringreader.peek() == '/') {
356 -@@ -547,6 +784,7 @@
357 +@@ -547,6 +785,7 @@
357 358 ParseResults<CommandListenerWrapper> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
358 359
359 360 this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
360 361 + if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
361 362 this.connection.send(new PacketPlayOutTabComplete(packetplayintabcomplete.getId(), suggestions));
362 363 });
363 364 }
364 -@@ -792,6 +1030,13 @@
365 +@@ -792,6 +1031,13 @@
365 366
366 367 if (container instanceof ContainerMerchant) {
367 368 ContainerMerchant containermerchant = (ContainerMerchant) container;
368 369 + // CraftBukkit start
369 370 + final org.bukkit.event.inventory.TradeSelectEvent tradeSelectEvent = CraftEventFactory.callTradeSelectEvent(this.player, i, containermerchant);
370 371 + if (tradeSelectEvent.isCancelled()) {
371 372 + this.player.getBukkitEntity().updateInventory();
372 373 + return;
373 374 + }
374 375 + // CraftBukkit end
375 376
376 377 if (!containermerchant.stillValid(this.player)) {
377 378 PlayerConnection.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, containermerchant);
378 -@@ -806,6 +1051,13 @@
379 +@@ -806,6 +1052,13 @@
379 380
380 381 @Override
381 382 public void handleEditBook(PacketPlayInBEdit packetplayinbedit) {
382 383 + // CraftBukkit start
383 384 + if (this.lastBookTick + 20 > MinecraftServer.currentTick) {
384 385 + this.disconnect("Book edited too quickly!");
385 386 + return;
386 387 + }
387 388 + this.lastBookTick = MinecraftServer.currentTick;
388 389 + // CraftBukkit end
389 390 int i = packetplayinbedit.getSlot();
390 391
391 392 if (PlayerInventory.isHotbarSlot(i) || i == 40) {
392 -@@ -814,7 +1066,7 @@
393 +@@ -814,7 +1067,7 @@
393 394
394 395 Objects.requireNonNull(list);
395 396 optional.ifPresent(list::add);
396 397 - Stream stream = packetplayinbedit.getPages().stream().limit(100L);
397 398 + Stream<String> stream = packetplayinbedit.getPages().stream().limit(100L); // CraftBukkit - decompile error
398 399
399 400 Objects.requireNonNull(list);
400 401 stream.forEach(list::add);
401 -@@ -832,7 +1084,7 @@
402 +@@ -832,7 +1085,7 @@
402 403 ItemStack itemstack = this.player.getInventory().getItem(i);
403 404
404 405 if (itemstack.is(Items.WRITABLE_BOOK)) {
405 406 - this.updateBookPages(list, UnaryOperator.identity(), itemstack);
406 407 + this.updateBookPages(list, UnaryOperator.identity(), itemstack.copy(), i, itemstack); // CraftBukkit
407 408 }
408 409 }
409 410
410 -@@ -857,16 +1109,16 @@
411 +@@ -857,16 +1110,16 @@
411 412
412 413 this.updateBookPages(list, (s) -> {
413 414 return IChatBaseComponent.ChatSerializer.toJson(IChatBaseComponent.literal(s));
414 415 - }, itemstack1);
415 416 - this.player.getInventory().setItem(i, itemstack1);
416 417 + }, itemstack1, i, itemstack); // CraftBukkit
417 418 + this.player.getInventory().setItem(i, itemstack); // CraftBukkit - event factory updates the hand book
418 419 }
419 420 }
420 421
421 422 - private void updateBookPages(List<FilteredText> list, UnaryOperator<String> unaryoperator, ItemStack itemstack) {
422 423 + private void updateBookPages(List<FilteredText> list, UnaryOperator<String> unaryoperator, ItemStack itemstack, int slot, ItemStack handItem) { // CraftBukkit
423 424 NBTTagList nbttaglist = new NBTTagList();
424 425
425 426 if (this.player.isTextFilteringEnabled()) {
426 427 - Stream stream = list.stream().map((filteredtext) -> {
427 428 + Stream<NBTTagString> stream = list.stream().map((filteredtext) -> { // CraftBukkit - decompile error
428 429 return NBTTagString.valueOf((String) unaryoperator.apply(filteredtext.filteredOrEmpty()));
429 430 });
430 431
431 -@@ -892,6 +1144,7 @@
432 +@@ -892,6 +1145,7 @@
432 433 }
433 434
434 435 itemstack.addTagElement("pages", nbttaglist);
435 436 + CraftEventFactory.handleEditBookEvent(player, slot, handItem, itemstack); // CraftBukkit
436 437 }
437 438
438 439 @Override
439 -@@ -928,7 +1181,7 @@
440 +@@ -928,7 +1182,7 @@
440 441 } else {
441 442 WorldServer worldserver = this.player.serverLevel();
442 443
443 444 - if (!this.player.wonGame) {
444 445 + if (!this.player.wonGame && !this.player.isImmobile()) { // CraftBukkit
445 446 if (this.tickCount == 0) {
446 447 this.resetPosition();
447 448 }
448 -@@ -938,7 +1191,7 @@
449 +@@ -938,7 +1192,7 @@
449 450 this.awaitingTeleportTime = this.tickCount;
450 451 this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
451 452 }
452 453 -
453 454 + this.allowedPlayerTicks = 20; // CraftBukkit
454 455 } else {
455 456 this.awaitingTeleportTime = this.tickCount;
456 457 double d0 = clampHorizontal(packetplayinflying.getX(this.player.getX()));
457 -@@ -950,7 +1203,15 @@
458 +@@ -950,7 +1204,15 @@
458 459 if (this.player.isPassenger()) {
459 460 this.player.absMoveTo(this.player.getX(), this.player.getY(), this.player.getZ(), f, f1);
460 461 this.player.serverLevel().getChunkSource().move(this.player);
461 462 + this.allowedPlayerTicks = 20; // CraftBukkit
462 463 } else {
463 464 + // CraftBukkit - Make sure the move is valid but then reset it for plugins to modify
464 465 + double prevX = player.getX();
465 466 + double prevY = player.getY();
466 467 + double prevZ = player.getZ();
467 468 + float prevYaw = player.getYRot();
468 469 + float prevPitch = player.getXRot();
469 470 + // CraftBukkit end
470 471 double d3 = this.player.getX();
471 472 double d4 = this.player.getY();
472 473 double d5 = this.player.getZ();
473 -@@ -969,15 +1230,33 @@
474 +@@ -969,15 +1231,33 @@
474 475 ++this.receivedMovePacketCount;
475 476 int i = this.receivedMovePacketCount - this.knownMovePacketCount;
476 477
477 478 - if (i > 5) {
478 479 + // CraftBukkit start - handle custom speeds and skipped ticks
479 480 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
480 481 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
481 482 + this.lastTick = (int) (System.currentTimeMillis() / 50);
482 483 +
483 484 + if (i > Math.max(this.allowedPlayerTicks, 5)) {
499 500 +
500 501 if (!this.player.isChangingDimension() && (!this.player.level().getGameRules().getBoolean(GameRules.RULE_DISABLE_ELYTRA_MOVEMENT_CHECK) || !this.player.isFallFlying())) {
501 502 float f2 = this.player.isFallFlying() ? 300.0F : 100.0F;
502 503
503 504 - if (d10 - d9 > (double) (f2 * (float) i) && !this.isSingleplayerOwner()) {
504 505 + if (d10 - d9 > Math.max(f2, Math.pow((double) (10.0F * (float) i * speed), 2)) && !this.isSingleplayerOwner()) {
505 506 + // CraftBukkit end
506 507 PlayerConnection.LOGGER.warn("{} moved too quickly! {},{},{}", new Object[]{this.player.getName().getString(), d6, d7, d8});
507 508 this.teleport(this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYRot(), this.player.getXRot());
508 509 return;
509 -@@ -998,6 +1277,7 @@
510 +@@ -998,6 +1278,7 @@
510 511 boolean flag1 = this.player.verticalCollisionBelow;
511 512
512 513 this.player.move(EnumMoveType.PLAYER, new Vec3D(d6, d7, d8));
513 514 + this.player.onGround = packetplayinflying.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
514 515 double d11 = d7;
515 516
516 517 d6 = d0 - this.player.getX();
517 -@@ -1016,9 +1296,70 @@
518 +@@ -1016,9 +1297,70 @@
518 519 }
519 520
520 521 if (!this.player.noPhysics && !this.player.isSleeping() && (flag2 && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew(worldserver, axisalignedbb, d0, d1, d2))) {
521 522 - this.teleport(d3, d4, d5, f, f1);
522 523 + 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.
523 524 this.player.doCheckFallDamage(this.player.getX() - d3, this.player.getY() - d4, this.player.getZ() - d5, packetplayinflying.isOnGround());
524 525 } else {
525 526 + // CraftBukkit start - fire PlayerMoveEvent
526 527 + // Reset to old location first
527 528 + this.player.absMoveTo(prevX, prevY, prevZ, prevYaw, prevPitch);
579 580 + if (!from.equals(this.getCraftPlayer().getLocation()) && this.justTeleported) {
580 581 + this.justTeleported = false;
581 582 + return;
582 583 + }
583 584 + }
584 585 + }
585 586 + // CraftBukkit end
586 587 this.player.absMoveTo(d0, d1, d2, f, f1);
587 588 this.clientIsFloating = d11 >= -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);
588 589 this.player.serverLevel().getChunkSource().move(this.player);
589 -@@ -1059,11 +1400,68 @@
590 +@@ -1059,11 +1401,68 @@
590 591 return true;
591 592 }
592 593
593 594 + // CraftBukkit start - Delegate to teleport(Location)
594 595 public void teleport(double d0, double d1, double d2, float f, float f1) {
595 596 - this.teleport(d0, d1, d2, f, f1, Collections.emptySet());
596 597 + this.teleport(d0, d1, d2, f, f1, PlayerTeleportEvent.TeleportCause.UNKNOWN);
597 598 + }
598 599 +
599 600 + public void teleport(double d0, double d1, double d2, float f, float f1, PlayerTeleportEvent.TeleportCause cause) {
649 650 + }
650 651 + if (Float.isNaN(f1)) {
651 652 + f1 = 0;
652 653 + }
653 654 +
654 655 + this.justTeleported = true;
655 656 + // CraftBukkit end
656 657 double d3 = set.contains(RelativeMovement.X) ? this.player.getX() : 0.0D;
657 658 double d4 = set.contains(RelativeMovement.Y) ? this.player.getY() : 0.0D;
658 659 double d5 = set.contains(RelativeMovement.Z) ? this.player.getZ() : 0.0D;
659 -@@ -1075,6 +1473,14 @@
660 +@@ -1075,6 +1474,14 @@
660 661 this.awaitingTeleport = 0;
661 662 }
662 663
663 664 + // CraftBukkit start - update last location
664 665 + this.lastPosX = this.awaitingPositionFromClient.x;
665 666 + this.lastPosY = this.awaitingPositionFromClient.y;
666 667 + this.lastPosZ = this.awaitingPositionFromClient.z;
667 668 + this.lastYaw = f;
668 669 + this.lastPitch = f1;
669 670 + // CraftBukkit end
670 671 +
671 672 this.awaitingTeleportTime = this.tickCount;
672 673 this.player.absMoveTo(d0, d1, d2, f, f1);
673 674 this.player.connection.send(new PacketPlayOutPosition(d0 - d3, d1 - d4, d2 - d5, f - f2, f1 - f3, set, this.awaitingTeleport));
674 -@@ -1083,6 +1489,7 @@
675 +@@ -1083,6 +1490,7 @@
675 676 @Override
676 677 public void handlePlayerAction(PacketPlayInBlockDig packetplayinblockdig) {
677 678 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinblockdig, this, this.player.serverLevel());
678 679 + if (this.player.isImmobile()) return; // CraftBukkit
679 680 BlockPosition blockposition = packetplayinblockdig.getPos();
680 681
681 682 this.player.resetLastActionTime();
682 -@@ -1093,14 +1500,46 @@
683 +@@ -1093,14 +1501,46 @@
683 684 if (!this.player.isSpectator()) {
684 685 ItemStack itemstack = this.player.getItemInHand(EnumHand.OFF_HAND);
685 686
686 687 - this.player.setItemInHand(EnumHand.OFF_HAND, this.player.getItemInHand(EnumHand.MAIN_HAND));
687 688 - this.player.setItemInHand(EnumHand.MAIN_HAND, itemstack);
688 689 + // CraftBukkit start - inspiration taken from DispenserRegistry (See SpigotCraft#394)
689 690 + CraftItemStack mainHand = CraftItemStack.asCraftMirror(itemstack);
690 691 + CraftItemStack offHand = CraftItemStack.asCraftMirror(this.player.getItemInHand(EnumHand.MAIN_HAND));
691 692 + PlayerSwapHandItemsEvent swapItemsEvent = new PlayerSwapHandItemsEvent(getCraftPlayer(), mainHand.clone(), offHand.clone());
692 693 + this.cserver.getPluginManager().callEvent(swapItemsEvent);
721 722 + if (this.dropCount >= 20) {
722 723 + LOGGER.warn(this.player.getScoreboardName() + " dropped their items too quickly!");
723 724 + this.disconnect("You dropped your items too quickly (Hacking?)");
724 725 + return;
725 726 + }
726 727 + }
727 728 + // CraftBukkit end
728 729 this.player.drop(false);
729 730 }
730 731
731 -@@ -1138,6 +1577,7 @@
732 +@@ -1138,6 +1578,7 @@
732 733 @Override
733 734 public void handleUseItemOn(PacketPlayInUseItem packetplayinuseitem) {
734 735 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinuseitem, this, this.player.serverLevel());
735 736 + if (this.player.isImmobile()) return; // CraftBukkit
736 737 this.player.connection.ackBlockChangesUpTo(packetplayinuseitem.getSequence());
737 738 WorldServer worldserver = this.player.serverLevel();
738 739 EnumHand enumhand = packetplayinuseitem.getHand();
739 -@@ -1161,6 +1601,7 @@
740 +@@ -1161,6 +1602,7 @@
740 741
741 742 if (blockposition.getY() < i) {
742 743 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)) {
743 744 + this.player.stopUsingItem(); // CraftBukkit - SPIGOT-4706
744 745 EnumInteractionResult enuminteractionresult = this.player.gameMode.useItemOn(this.player, worldserver, itemstack, enumhand, movingobjectpositionblock);
745 746
746 747 if (enumdirection == EnumDirection.UP && !enuminteractionresult.consumesAction() && blockposition.getY() >= i - 1 && wasBlockPlacementAttempt(this.player, itemstack)) {
747 -@@ -1189,6 +1630,7 @@
748 +@@ -1189,6 +1631,7 @@
748 749 @Override
749 750 public void handleUseItem(PacketPlayInBlockPlace packetplayinblockplace) {
750 751 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinblockplace, this, this.player.serverLevel());
751 752 + if (this.player.isImmobile()) return; // CraftBukkit
752 753 this.ackBlockChangesUpTo(packetplayinblockplace.getSequence());
753 754 WorldServer worldserver = this.player.serverLevel();
754 755 EnumHand enumhand = packetplayinblockplace.getHand();
755 -@@ -1196,6 +1638,49 @@
756 +@@ -1196,6 +1639,49 @@
756 757
757 758 this.player.resetLastActionTime();
758 759 if (!itemstack.isEmpty() && itemstack.isItemEnabled(worldserver.enabledFeatures())) {
759 760 + // CraftBukkit start
760 761 + // Raytrace to look for 'rogue armswings'
761 762 + float f1 = this.player.getXRot();
762 763 + float f2 = this.player.getYRot();
763 764 + double d0 = this.player.getX();
764 765 + double d1 = this.player.getY() + (double) this.player.getEyeHeight();
765 766 + double d2 = this.player.getZ();
795 796 + return;
796 797 + }
797 798 + itemstack = this.player.getItemInHand(enumhand); // Update in case it was changed in the event
798 799 + if (itemstack.isEmpty()) {
799 800 + return;
800 801 + }
801 802 + // CraftBukkit end
802 803 EnumInteractionResult enuminteractionresult = this.player.gameMode.useItem(this.player, worldserver, itemstack, enumhand);
803 804
804 805 if (enuminteractionresult.shouldSwing()) {
805 -@@ -1216,7 +1701,7 @@
806 +@@ -1216,7 +1702,7 @@
806 807 Entity entity = packetplayinspectate.getEntity(worldserver);
807 808
808 809 if (entity != null) {
809 810 - this.player.teleportTo(worldserver, entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot());
810 811 + this.player.teleportTo(worldserver, entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot(), org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.SPECTATE); // CraftBukkit
811 812 return;
812 813 }
813 814 }
814 -@@ -1231,6 +1716,7 @@
815 +@@ -1231,6 +1717,7 @@
815 816 PlayerConnection.LOGGER.info("Disconnecting {} due to resource pack rejection", this.player.getName());
816 817 this.disconnect(IChatBaseComponent.translatable("multiplayer.requiredTexturePrompt.disconnect"));
817 818 }
818 819 + this.cserver.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(getCraftPlayer(), PlayerResourcePackStatusEvent.Status.values()[packetplayinresourcepackstatus.action.ordinal()])); // CraftBukkit
819 820
820 821 }
821 822
822 -@@ -1252,12 +1738,27 @@
823 +@@ -1252,12 +1739,27 @@
823 824
824 825 @Override
825 826 public void onDisconnect(IChatBaseComponent ichatbasecomponent) {
826 827 + // CraftBukkit start - Rarely it would send a disconnect line twice
827 828 + if (this.processedDisconnect) {
828 829 + return;
829 830 + } else {
830 831 + this.processedDisconnect = true;
831 832 + }
832 833 + // CraftBukkit end
841 842 this.player.disconnect();
842 843 - this.server.getPlayerList().remove(this.player);
843 844 + String quitMessage = this.server.getPlayerList().remove(this.player);
844 845 + if ((quitMessage != null) && (quitMessage.length() > 0)) {
845 846 + this.server.getPlayerList().broadcastMessage(CraftChatMessage.fromString(quitMessage));
846 847 + }
847 848 + // CraftBukkit end
848 849 this.player.getTextFilter().leave();
849 850 if (this.isSingleplayerOwner()) {
850 851 PlayerConnection.LOGGER.info("Stopping singleplayer server as player logged out");
851 -@@ -1280,6 +1781,15 @@
852 +@@ -1280,6 +1782,15 @@
852 853 }
853 854
854 855 public void send(Packet<?> packet, @Nullable PacketSendListener packetsendlistener) {
855 856 + // CraftBukkit start
856 857 + if (packet == null) {
857 858 + return;
858 859 + } else if (packet instanceof PacketPlayOutSpawnPosition) {
859 860 + PacketPlayOutSpawnPosition packet6 = (PacketPlayOutSpawnPosition) packet;
860 861 + this.player.compassTarget = CraftLocation.toBukkit(packet6.pos, this.getCraftPlayer().getWorld());
861 862 + }
862 863 + // CraftBukkit end
863 864 +
864 865 try {
865 866 this.connection.send(packet, packetsendlistener);
866 867 } catch (Throwable throwable) {
867 -@@ -1296,7 +1806,16 @@
868 +@@ -1296,7 +1807,16 @@
868 869 @Override
869 870 public void handleSetCarriedItem(PacketPlayInHeldItemSlot packetplayinhelditemslot) {
870 871 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinhelditemslot, this, this.player.serverLevel());
871 872 + if (this.player.isImmobile()) return; // CraftBukkit
872 873 if (packetplayinhelditemslot.getSlot() >= 0 && packetplayinhelditemslot.getSlot() < PlayerInventory.getSelectionSize()) {
873 874 + PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getCraftPlayer(), this.player.getInventory().selected, packetplayinhelditemslot.getSlot());
874 875 + this.cserver.getPluginManager().callEvent(event);
875 876 + if (event.isCancelled()) {
876 877 + this.send(new PacketPlayOutHeldItemSlot(this.player.getInventory().selected));
877 878 + this.player.resetLastActionTime();
878 879 + return;
879 880 + }
880 881 + // CraftBukkit end
881 882 if (this.player.getInventory().selected != packetplayinhelditemslot.getSlot() && this.player.getUsedItemHand() == EnumHand.MAIN_HAND) {
882 883 this.player.stopUsingItem();
883 884 }
884 -@@ -1305,18 +1824,25 @@
885 +@@ -1305,18 +1825,25 @@
885 886 this.player.resetLastActionTime();
886 887 } else {
887 888 PlayerConnection.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString());
888 889 + this.disconnect("Invalid hotbar selection (Hacking?)"); // CraftBukkit
889 890 }
890 891 }
891 892
892 893 @Override
893 894 public void handleChat(PacketPlayInChat packetplayinchat) {
894 895 + // CraftBukkit start - async chat
901 902 this.disconnect(IChatBaseComponent.translatable("multiplayer.disconnect.illegal_characters"));
902 903 } else {
903 904 Optional<LastSeenMessages> optional = this.tryHandleChat(packetplayinchat.message(), packetplayinchat.timeStamp(), packetplayinchat.lastSeenMessages());
904 905
905 906 if (optional.isPresent()) {
906 907 - this.server.submit(() -> {
907 908 + // this.server.submit(() -> { // CraftBukkit - async chat
908 909 PlayerChatMessage playerchatmessage;
909 910
910 911 try {
911 -@@ -1334,9 +1860,9 @@
912 +@@ -1334,9 +1861,9 @@
912 913 PlayerChatMessage playerchatmessage1 = playerchatmessage.withUnsignedContent((IChatBaseComponent) completablefuture1.join()).filter(((FilteredText) completablefuture.join()).mask());
913 914
914 915 this.broadcastChatMessage(playerchatmessage1);
915 916 - }, executor);
916 917 + }, this.server.chatExecutor); // CraftBukkit - async chat
917 918 });
918 919 - });
919 920 + // }); // CraftBukkit - async chat
920 921 }
921 922
922 923 }
923 -@@ -1351,6 +1877,12 @@
924 +@@ -1351,6 +1878,12 @@
924 925
925 926 if (optional.isPresent()) {
926 927 this.server.submit(() -> {
927 928 + // CraftBukkit start - SPIGOT-7346: Prevent disconnected players from executing commands
928 929 + if (player.hasDisconnected()) {
929 930 + return;
930 931 + }
931 932 + // CraftBukkit end
932 933 +
933 934 this.performChatCommand(serverboundchatcommandpacket, (LastSeenMessages) optional.get());
934 935 this.detectRateSpam();
935 936 });
936 -@@ -1360,12 +1892,25 @@
937 +@@ -1360,12 +1893,25 @@
937 938 }
938 939
939 940 private void performChatCommand(ServerboundChatCommandPacket serverboundchatcommandpacket, LastSeenMessages lastseenmessages) {
940 941 - ParseResults parseresults = this.parseCommand(serverboundchatcommandpacket.command());
941 942 + // CraftBukkit start
942 943 + String command = "/" + serverboundchatcommandpacket.command();
943 944 + PlayerConnection.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + command);
944 945 +
945 946 + PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(getCraftPlayer(), command, new LazyPlayerSet(server));
946 947 + this.cserver.getPluginManager().callEvent(event);
954 955 + // CraftBukkit end
955 956
956 957 Map map;
957 958
958 959 try {
959 960 - map = this.collectSignedArguments(serverboundchatcommandpacket, SignableCommand.of(parseresults), lastseenmessages);
960 961 + map = (serverboundchatcommandpacket.command().equals(command)) ? this.collectSignedArguments(serverboundchatcommandpacket, SignableCommand.of(parseresults), lastseenmessages) : Collections.emptyMap(); // CraftBukkit
961 962 } catch (SignedMessageChain.a signedmessagechain_a) {
962 963 this.handleMessageDecodeFailure(signedmessagechain_a);
963 964 return;
964 -@@ -1373,10 +1918,10 @@
965 +@@ -1373,10 +1919,10 @@
965 966
966 967 CommandSigningContext.a commandsigningcontext_a = new CommandSigningContext.a(map);
967 968
968 969 - parseresults = CommandDispatcher.mapSource(parseresults, (commandlistenerwrapper) -> {
969 970 + parseresults = CommandDispatcher.<CommandListenerWrapper>mapSource(parseresults, (commandlistenerwrapper) -> { // CraftBukkit - decompile error
970 971 return commandlistenerwrapper.withSigningContext(commandsigningcontext_a);
971 972 });
972 973 - this.server.getCommands().performCommand(parseresults, serverboundchatcommandpacket.command());
973 974 + this.server.getCommands().performCommand(parseresults, command); // CraftBukkit
974 975 }
975 976
976 977 private void handleMessageDecodeFailure(SignedMessageChain.a signedmessagechain_a) {
977 -@@ -1417,7 +1962,7 @@
978 +@@ -1417,7 +1963,7 @@
978 979 } else {
979 980 Optional<LastSeenMessages> optional = this.unpackAndApplyLastSeen(lastseenmessages_b);
980 981
981 982 - if (this.player.getChatVisibility() == EnumChatVisibility.HIDDEN) {
982 983 + if (this.player.isRemoved() || this.player.getChatVisibility() == EnumChatVisibility.HIDDEN) { // CraftBukkit - dead men tell no tales
983 984 this.send(new ClientboundSystemChatPacket(IChatBaseComponent.translatable("chat.disabled.options").withStyle(EnumChatFormat.RED), false));
984 985 return Optional.empty();
985 986 } else {
986 -@@ -1465,6 +2010,122 @@
987 +@@ -1465,6 +2011,122 @@
987 988 return false;
988 989 }
989 990
990 991 + // CraftBukkit start - add method
991 992 + public void chat(String s, PlayerChatMessage original, boolean async) {
992 993 + if (s.isEmpty() || this.player.getChatVisibility() == EnumChatVisibility.HIDDEN) {
993 994 + return;
994 995 + }
995 996 + OutgoingChatMessage outgoing = OutgoingChatMessage.create(original);
996 997 +
1099 1100 + player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command");
1100 1101 + java.util.logging.Logger.getLogger(PlayerConnection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
1101 1102 + return;
1102 1103 + }
1103 1104 + }
1104 1105 + // CraftBukkit end
1105 1106 +
1106 1107 private PlayerChatMessage getSignedMessage(PacketPlayInChat packetplayinchat, LastSeenMessages lastseenmessages) throws SignedMessageChain.a {
1107 1108 SignedMessageBody signedmessagebody = new SignedMessageBody(packetplayinchat.message(), packetplayinchat.timeStamp(), packetplayinchat.salt(), lastseenmessages);
1108 1109
1109 -@@ -1472,13 +2133,33 @@
1110 +@@ -1472,13 +2134,33 @@
1110 1111 }
1111 1112
1112 1113 private void broadcastChatMessage(PlayerChatMessage playerchatmessage) {
1113 1114 - this.server.getPlayerList().broadcastChatMessage(playerchatmessage, this.player, ChatMessageType.bind(ChatMessageType.CHAT, (Entity) this.player));
1114 1115 + // CraftBukkit start
1115 1116 + String s = playerchatmessage.signedContent();
1116 1117 + if (s.isEmpty()) {
1117 1118 + LOGGER.warn(this.player.getScoreboardName() + " tried to send an empty message");
1118 1119 + } else if (getCraftPlayer().isConversing()) {
1119 1120 + final String conversationInput = s;
1136 1137 private void detectRateSpam() {
1137 1138 - this.chatSpamTickCount += 20;
1138 1139 - if (this.chatSpamTickCount > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
1139 1140 + // CraftBukkit start - replaced with thread safe throttle
1140 1141 + // this.chatSpamTickCount += 20;
1141 1142 + if (this.chatSpamTickCount.addAndGet(20) > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
1142 1143 + // CraftBukkit end
1143 1144 this.disconnect(IChatBaseComponent.translatable("disconnect.spam"));
1144 1145 }
1145 1146
1146 -@@ -1500,13 +2181,59 @@
1147 +@@ -1500,13 +2182,62 @@
1147 1148 @Override
1148 1149 public void handleAnimate(PacketPlayInArmAnimation packetplayinarmanimation) {
1149 1150 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinarmanimation, this, this.player.serverLevel());
1150 1151 + if (this.player.isImmobile()) return; // CraftBukkit
1151 1152 this.player.resetLastActionTime();
1152 1153 + // CraftBukkit start - Raytrace to look for 'rogue armswings'
1153 1154 + float f1 = this.player.getXRot();
1154 1155 + float f2 = this.player.getYRot();
1155 1156 + double d0 = this.player.getX();
1156 1157 + double d1 = this.player.getY() + (double) this.player.getEyeHeight();
1157 1158 + double d2 = this.player.getZ();
1158 1159 + Location origin = new Location(this.player.level().getWorld(), d0, d1, d2, f2, f1);
1159 1160 +
1160 1161 + double d3 = player.gameMode.getGameModeForPlayer() == EnumGamemode.CREATIVE ? 5.0D : 4.5D;
1161 1162 + // 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
1162 -+ org.bukkit.util.RayTraceResult result = this.player.level().getWorld().rayTrace(origin, origin.getDirection(), d3, org.bukkit.FluidCollisionMode.NEVER, false, 0.1, entity -> entity != this.player.getBukkitEntity() && this.player.getBukkitEntity().canSee(entity));
1163 -+
1163 ++ // SPIGOT-7429: Make sure to call PlayerInteractEvent for spectators and non-pickable entities
1164 ++ org.bukkit.util.RayTraceResult result = this.player.level().getWorld().rayTrace(origin, origin.getDirection(), d3, org.bukkit.FluidCollisionMode.NEVER, false, 0.1, entity -> {
1165 ++ Entity handle = ((CraftEntity) entity).getHandle();
1166 ++ return entity != this.player.getBukkitEntity() && this.player.getBukkitEntity().canSee(entity) && !handle.isSpectator() && handle.isPickable() && !handle.isPassengerOfSameVehicle(player);
1167 ++ });
1164 1168 + if (result == null) {
1165 1169 + CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_AIR, this.player.getInventory().getSelected(), EnumHand.MAIN_HAND);
1166 1170 + }
1167 1171 +
1168 1172 + // Arm swing animation
1169 1173 + PlayerAnimationEvent event = new PlayerAnimationEvent(this.getCraftPlayer(), (packetplayinarmanimation.getHand() == EnumHand.MAIN_HAND) ? PlayerAnimationType.ARM_SWING : PlayerAnimationType.OFF_ARM_SWING);
1170 1174 + this.cserver.getPluginManager().callEvent(event);
1171 1175 +
1172 1176 + if (event.isCancelled()) return;
1173 1177 + // CraftBukkit end
1196 1200 +
1197 1201 + if (e2.isCancelled()) {
1198 1202 + return;
1199 1203 + }
1200 1204 + break;
1201 1205 + }
1202 1206 + // CraftBukkit end
1203 1207 this.player.resetLastActionTime();
1204 1208 Entity entity;
1205 1209 IJumpable ijumpable;
1206 -@@ -1588,6 +2315,12 @@
1210 +@@ -1588,6 +2319,12 @@
1207 1211 }
1208 1212
1209 1213 public void sendPlayerChatMessage(PlayerChatMessage playerchatmessage, ChatMessageType.a chatmessagetype_a) {
1210 1214 + // 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).
1211 1215 + if (!getCraftPlayer().canSee(playerchatmessage.link().sender())) {
1212 1216 + sendDisguisedChatMessage(playerchatmessage.decoratedContent(), chatmessagetype_a);
1213 1217 + return;
1214 1218 + }
1215 1219 + // CraftBukkit end
1216 1220 this.send(new ClientboundPlayerChatPacket(playerchatmessage.link().sender(), playerchatmessage.link().index(), playerchatmessage.signature(), playerchatmessage.signedBody().pack(this.messageSignatureCache), playerchatmessage.unsignedContent(), playerchatmessage.filterMask(), chatmessagetype_a.toNetwork(this.player.level().registryAccess())));
1217 1221 this.addPendingMessage(playerchatmessage);
1218 1222 }
1219 -@@ -1603,6 +2336,7 @@
1223 +@@ -1603,6 +2340,7 @@
1220 1224 @Override
1221 1225 public void handleInteract(PacketPlayInUseEntity packetplayinuseentity) {
1222 1226 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinuseentity, this, this.player.serverLevel());
1223 1227 + if (this.player.isImmobile()) return; // CraftBukkit
1224 1228 final WorldServer worldserver = this.player.serverLevel();
1225 1229 final Entity entity = packetplayinuseentity.getTarget(worldserver);
1226 1230
1227 -@@ -1617,13 +2351,51 @@
1231 +@@ -1617,13 +2355,51 @@
1228 1232
1229 1233 if (axisalignedbb.distanceToSqr(this.player.getEyePosition()) < PlayerConnection.MAX_INTERACTION_DISTANCE) {
1230 1234 packetplayinuseentity.dispatch(new PacketPlayInUseEntity.c() {
1231 1235 - private void performInteraction(EnumHand enumhand, PlayerConnection.a playerconnection_a) {
1232 1236 + private void performInteraction(EnumHand enumhand, PlayerConnection.a playerconnection_a, PlayerInteractEntityEvent event) { // CraftBukkit
1233 1237 ItemStack itemstack = PlayerConnection.this.player.getItemInHand(enumhand);
1234 1238
1235 1239 if (itemstack.isItemEnabled(worldserver.enabledFeatures())) {
1236 1240 ItemStack itemstack1 = itemstack.copy();
1237 1241 + // CraftBukkit start
1270 1274
1271 1275 + // CraftBukkit start
1272 1276 + if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) {
1273 1277 + player.containerMenu.sendAllDataToRemote();
1274 1278 + }
1275 1279 + // CraftBukkit end
1276 1280 +
1277 1281 if (enuminteractionresult.consumesAction()) {
1278 1282 CriterionTriggers.PLAYER_INTERACTED_WITH_ENTITY.trigger(PlayerConnection.this.player, itemstack1, entity);
1279 1283 if (enuminteractionresult.shouldSwing()) {
1280 -@@ -1636,23 +2408,29 @@
1284 +@@ -1636,23 +2412,29 @@
1281 1285
1282 1286 @Override
1283 1287 public void onInteraction(EnumHand enumhand) {
1284 1288 - this.performInteraction(enumhand, EntityHuman::interactOn);
1285 1289 + this.performInteraction(enumhand, EntityHuman::interactOn, new PlayerInteractEntityEvent(getCraftPlayer(), entity.getBukkitEntity(), (enumhand == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND)); // CraftBukkit
1286 1290 }
1287 1291
1288 1292 @Override
1289 1293 public void onInteraction(EnumHand enumhand, Vec3D vec3d) {
1290 1294 this.performInteraction(enumhand, (entityplayer, entity1, enumhand1) -> {
1303 1307 if (itemstack.isItemEnabled(worldserver.enabledFeatures())) {
1304 1308 PlayerConnection.this.player.attack(entity);
1305 1309 + // CraftBukkit start
1306 1310 + if (!itemstack.isEmpty() && itemstack.getCount() <= -1) {
1307 1311 + player.containerMenu.sendAllDataToRemote();
1308 1312 + }
1309 1313 + // CraftBukkit end
1310 1314 }
1311 1315 } else {
1312 1316 PlayerConnection.this.disconnect(IChatBaseComponent.translatable("multiplayer.disconnect.invalid_entity_attacked"));
1313 -@@ -1675,14 +2453,14 @@
1317 +@@ -1675,14 +2457,14 @@
1314 1318 case PERFORM_RESPAWN:
1315 1319 if (this.player.wonGame) {
1316 1320 this.player.wonGame = false;
1317 1321 - this.player = this.server.getPlayerList().respawn(this.player, true);
1318 1322 + this.player = this.server.getPlayerList().respawn(this.player, true, RespawnReason.END_PORTAL);
1319 1323 CriterionTriggers.CHANGED_DIMENSION.trigger(this.player, World.END, World.OVERWORLD);
1320 1324 } else {
1321 1325 if (this.player.getHealth() > 0.0F) {
1322 1326 return;
1323 1327 }
1324 1328
1325 1329 - this.player = this.server.getPlayerList().respawn(this.player, false);
1326 1330 + this.player = this.server.getPlayerList().respawn(this.player, false, RespawnReason.DEATH);
1327 1331 if (this.server.isHardcore()) {
1328 1332 this.player.setGameMode(EnumGamemode.SPECTATOR);
1329 1333 ((GameRules.GameRuleBoolean) this.player.level().getGameRules().getRule(GameRules.RULE_SPECTATORSGENERATECHUNKS)).set(false, this.server);
1330 -@@ -1698,15 +2476,21 @@
1334 +@@ -1698,15 +2480,21 @@
1331 1335 @Override
1332 1336 public void handleContainerClose(PacketPlayInCloseWindow packetplayinclosewindow) {
1333 1337 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinclosewindow, this, this.player.serverLevel());
1334 1338 +
1335 1339 + if (this.player.isImmobile()) return; // CraftBukkit
1336 1340 + CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit
1337 1341 +
1338 1342 this.player.doCloseContainer();
1339 1343 }
1340 1344
1344 1348 + if (this.player.isImmobile()) return; // CraftBukkit
1345 1349 this.player.resetLastActionTime();
1346 1350 - if (this.player.containerMenu.containerId == packetplayinwindowclick.getContainerId()) {
1347 1351 - if (this.player.isSpectator()) {
1348 1352 + if (this.player.containerMenu.containerId == packetplayinwindowclick.getContainerId() && this.player.containerMenu.stillValid(this.player)) { // CraftBukkit
1349 1353 + boolean cancelled = this.player.isSpectator(); // CraftBukkit - see below if
1350 1354 + if (false/*this.player.isSpectator()*/) { // CraftBukkit
1351 1355 this.player.containerMenu.sendAllDataToRemote();
1352 1356 } else if (!this.player.containerMenu.stillValid(this.player)) {
1353 1357 PlayerConnection.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu);
1354 -@@ -1719,7 +2503,284 @@
1358 +@@ -1719,7 +2507,284 @@
1355 1359 boolean flag = packetplayinwindowclick.getStateId() != this.player.containerMenu.getStateId();
1356 1360
1357 1361 this.player.containerMenu.suppressRemoteUpdates();
1358 1362 - this.player.containerMenu.clicked(i, packetplayinwindowclick.getButtonNum(), packetplayinwindowclick.getClickType(), this.player);
1359 1363 + // CraftBukkit start - Call InventoryClickEvent
1360 1364 + if (packetplayinwindowclick.getSlotNum() < -1 && packetplayinwindowclick.getSlotNum() != -999) {
1361 1365 + return;
1362 1366 + }
1363 1367 +
1364 1368 + InventoryView inventory = this.player.containerMenu.getBukkitView();
1630 1634 + if (event instanceof CraftItemEvent || event instanceof SmithItemEvent) {
1631 1635 + // Need to update the inventory on crafting to
1632 1636 + // correctly support custom recipes
1633 1637 + player.containerMenu.sendAllDataToRemote();
1634 1638 + }
1635 1639 + }
1636 1640 + // CraftBukkit end
1637 1641 ObjectIterator objectiterator = Int2ObjectMaps.fastIterable(packetplayinwindowclick.getChangedSlots()).iterator();
1638 1642
1639 1643 while (objectiterator.hasNext()) {
1640 -@@ -1759,6 +2820,7 @@
1644 +@@ -1759,6 +2824,7 @@
1641 1645 @Override
1642 1646 public void handleContainerButtonClick(PacketPlayInEnchantItem packetplayinenchantitem) {
1643 1647 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinenchantitem, this, this.player.serverLevel());
1644 1648 + if (this.player.isImmobile()) return; // CraftBukkit
1645 1649 this.player.resetLastActionTime();
1646 1650 if (this.player.containerMenu.containerId == packetplayinenchantitem.getContainerId() && !this.player.isSpectator()) {
1647 1651 if (!this.player.containerMenu.stillValid(this.player)) {
1648 -@@ -1801,6 +2863,43 @@
1652 +@@ -1801,6 +2867,43 @@
1649 1653
1650 1654 boolean flag1 = packetplayinsetcreativeslot.getSlotNum() >= 1 && packetplayinsetcreativeslot.getSlotNum() <= 45;
1651 1655 boolean flag2 = itemstack.isEmpty() || itemstack.getDamageValue() >= 0 && itemstack.getCount() <= 64 && !itemstack.isEmpty();
1652 1656 + if (flag || (flag1 && !ItemStack.matches(this.player.inventoryMenu.getSlot(packetplayinsetcreativeslot.getSlotNum()).getItem(), packetplayinsetcreativeslot.getItem()))) { // Insist on valid slot
1653 1657 + // CraftBukkit start - Call click event
1654 1658 + InventoryView inventory = this.player.inventoryMenu.getBukkitView();
1655 1659 + org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packetplayinsetcreativeslot.getItem());
1656 1660 +
1657 1661 + SlotType type = SlotType.QUICKBAR;
1658 1662 + if (flag) {
1682 1686 + this.player.connection.send(new PacketPlayOutSetSlot(this.player.inventoryMenu.containerId, this.player.inventoryMenu.incrementStateId(), packetplayinsetcreativeslot.getSlotNum(), this.player.inventoryMenu.getSlot(packetplayinsetcreativeslot.getSlotNum()).getItem()));
1683 1687 + this.player.connection.send(new PacketPlayOutSetSlot(-1, this.player.inventoryMenu.incrementStateId(), -1, ItemStack.EMPTY));
1684 1688 + }
1685 1689 + return;
1686 1690 + }
1687 1691 + }
1688 1692 + // CraftBukkit end
1689 1693
1690 1694 if (flag1 && flag2) {
1691 1695 this.player.inventoryMenu.getSlot(packetplayinsetcreativeslot.getSlotNum()).setByPlayer(itemstack);
1692 -@@ -1823,6 +2922,7 @@
1696 +@@ -1823,6 +2926,7 @@
1693 1697 }
1694 1698
1695 1699 private void updateSignText(PacketPlayInUpdateSign packetplayinupdatesign, List<FilteredText> list) {
1696 1700 + if (this.player.isImmobile()) return; // CraftBukkit
1697 1701 this.player.resetLastActionTime();
1698 1702 WorldServer worldserver = this.player.serverLevel();
1699 1703 BlockPosition blockposition = packetplayinupdatesign.getPos();
1700 -@@ -1843,6 +2943,7 @@
1704 +@@ -1843,6 +2947,7 @@
1701 1705
1702 1706 @Override
1703 1707 public void handleKeepAlive(PacketPlayInKeepAlive packetplayinkeepalive) {
1704 1708 + PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinkeepalive, this, this.player.serverLevel()); // CraftBukkit
1705 1709 if (this.keepAlivePending && packetplayinkeepalive.getId() == this.keepAliveChallenge) {
1706 1710 int i = (int) (SystemUtils.getMillis() - this.keepAliveTime);
1707 1711
1708 -@@ -1857,7 +2958,17 @@
1712 +@@ -1857,7 +2962,17 @@
1709 1713 @Override
1710 1714 public void handlePlayerAbilities(PacketPlayInAbilities packetplayinabilities) {
1711 1715 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinabilities, this, this.player.serverLevel());
1712 1716 - this.player.getAbilities().flying = packetplayinabilities.isFlying() && this.player.getAbilities().mayfly;
1713 1717 + // CraftBukkit start
1714 1718 + if (this.player.getAbilities().mayfly && this.player.getAbilities().flying != packetplayinabilities.isFlying()) {
1715 1719 + PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.player.getBukkitEntity(), packetplayinabilities.isFlying());
1716 1720 + this.cserver.getPluginManager().callEvent(event);
1717 1721 + if (!event.isCancelled()) {
1718 1722 + this.player.getAbilities().flying = packetplayinabilities.isFlying(); // Actually set the player's flying status
1719 1723 + } else {
1720 1724 + this.player.onUpdateAbilities(); // Tell the player their ability was reverted
1721 1725 + }
1722 1726 + }
1723 1727 + // CraftBukkit end
1724 1728 }
1725 1729
1726 1730 @Override
1727 -@@ -1866,8 +2977,50 @@
1731 +@@ -1866,8 +2981,50 @@
1728 1732 this.player.updateOptions(packetplayinsettings);
1729 1733 }
1730 1734
1731 1735 - @Override
1732 1736 - public void handleCustomPayload(PacketPlayInCustomPayload packetplayincustompayload) {}
1733 1737 + // CraftBukkit start
1734 1738 + private static final MinecraftKey CUSTOM_REGISTER = new MinecraftKey("register");
1735 1739 + private static final MinecraftKey CUSTOM_UNREGISTER = new MinecraftKey("unregister");
1736 1740 +
1737 1741 + @Override

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

Add shortcut