Commits

md_5 authored 9dafd1092f1
Don't send updates over large distances
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 -@@ -156,6 +156,63 @@
3 +@@ -156,6 +156,62 @@
4 4 import org.apache.logging.log4j.LogManager;
5 5 import org.apache.logging.log4j.Logger;
6 6
7 7 +// CraftBukkit start
8 8 +import java.util.concurrent.ExecutionException;
9 9 +import java.util.concurrent.atomic.AtomicInteger;
10 10 +import net.minecraft.network.protocol.game.PacketPlayOutAttachEntity;
11 11 +import net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata;
12 12 +import net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving;
13 13 +import net.minecraft.network.protocol.game.PacketPlayOutSpawnPosition;
51 51 +import org.bukkit.event.player.PlayerResourcePackStatusEvent;
52 52 +import org.bukkit.event.player.PlayerSwapHandItemsEvent;
53 53 +import org.bukkit.event.player.PlayerTeleportEvent;
54 54 +import org.bukkit.event.player.PlayerToggleFlightEvent;
55 55 +import org.bukkit.event.player.PlayerToggleSneakEvent;
56 56 +import org.bukkit.event.player.PlayerToggleSprintEvent;
57 57 +import org.bukkit.inventory.CraftingInventory;
58 58 +import org.bukkit.inventory.EquipmentSlot;
59 59 +import org.bukkit.inventory.InventoryView;
60 60 +import org.bukkit.inventory.SmithingInventory;
61 -+import org.bukkit.util.NumberConversions;
62 61 +// CraftBukkit end
63 62 +
64 63 public class PlayerConnection implements ServerPlayerConnection, PacketListenerPlayIn {
65 64
66 65 static final Logger LOGGER = LogManager.getLogger();
67 -@@ -167,7 +224,9 @@
66 +@@ -167,7 +223,9 @@
68 67 private long keepAliveTime;
69 68 private boolean keepAlivePending;
70 69 private long keepAliveChallenge;
71 70 - private int chatSpamTickCount;
72 71 + // CraftBukkit start - multithreaded fields
73 72 + private final AtomicInteger chatSpamTickCount = new AtomicInteger();
74 73 + // CraftBukkit end
75 74 private int dropSpamTickCount;
76 75 private double firstGoodX;
77 76 private double firstGoodY;
78 -@@ -202,7 +261,33 @@
77 +@@ -202,7 +260,31 @@
79 78 entityplayer.connection = this;
80 79 this.keepAliveTime = SystemUtils.getMillis();
81 80 entityplayer.getTextFilter().join();
82 81 +
83 82 + // CraftBukkit start - add fields and methods
84 83 + this.cserver = minecraftserver.server;
85 84 + }
86 85 +
87 86 + private final org.bukkit.craftbukkit.CraftServer cserver;
88 87 + public boolean processedDisconnect;
89 88 + private int lastTick = MinecraftServer.currentTick;
90 89 + private int allowedPlayerTicks = 1;
91 90 + private int lastDropTick = MinecraftServer.currentTick;
92 91 + private int lastBookTick = MinecraftServer.currentTick;
93 92 + private int dropCount = 0;
94 -+ private static final int SURVIVAL_PLACE_DISTANCE_SQUARED = 6 * 6;
95 -+ private static final int CREATIVE_PLACE_DISTANCE_SQUARED = 7 * 7;
96 93 +
97 94 + // Get position of last block hit for BlockDamageLevel.STOPPED
98 95 + private double lastPosX = Double.MAX_VALUE;
99 96 + private double lastPosY = Double.MAX_VALUE;
100 97 + private double lastPosZ = Double.MAX_VALUE;
101 98 + private float lastPitch = Float.MAX_VALUE;
102 99 + private float lastYaw = Float.MAX_VALUE;
103 100 + private boolean justTeleported = false;
104 101 +
105 102 + public CraftPlayer getCraftPlayer() {
106 103 + return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity();
107 104 }
108 105 + // CraftBukkit end
109 106
110 107 public void tick() {
111 108 this.resetPosition();
112 -@@ -251,7 +336,7 @@
109 +@@ -251,7 +333,7 @@
113 110 this.server.getProfiler().push("keepAlive");
114 111 long i = SystemUtils.getMillis();
115 112
116 113 - if (i - this.keepAliveTime >= 15000L) {
117 114 + if (i - this.keepAliveTime >= 25000L) { // CraftBukkit
118 115 if (this.keepAlivePending) {
119 116 this.disconnect(new ChatMessage("disconnect.timeout"));
120 117 } else {
121 -@@ -263,15 +348,21 @@
118 +@@ -263,15 +345,21 @@
122 119 }
123 120
124 121 this.server.getProfiler().pop();
125 122 + // CraftBukkit start
126 123 + for (int spam; (spam = this.chatSpamTickCount.get()) > 0 && !chatSpamTickCount.compareAndSet(spam, spam - 1); ) ;
127 124 + /* Use thread-safe field access instead
128 125 if (this.chatSpamTickCount > 0) {
129 126 --this.chatSpamTickCount;
130 127 }
131 128 + */
132 129 + // CraftBukkit end
133 130
134 131 if (this.dropSpamTickCount > 0) {
135 132 --this.dropSpamTickCount;
136 133 }
137 134
138 135 if (this.player.getLastActionTime() > 0L && this.server.getPlayerIdleTimeout() > 0 && SystemUtils.getMillis() - this.player.getLastActionTime() > (long) (this.server.getPlayerIdleTimeout() * 1000 * 60)) {
139 136 + this.player.resetLastActionTime(); // CraftBukkit - SPIGOT-854
140 137 this.disconnect(new ChatMessage("multiplayer.disconnect.idling"));
141 138 }
142 139
143 -@@ -295,16 +386,46 @@
140 +@@ -295,16 +383,46 @@
144 141 return this.server.isSingleplayerOwner(this.player.getGameProfile());
145 142 }
146 143
147 144 + // CraftBukkit start
148 145 + @Deprecated
149 146 public void disconnect(IChatBaseComponent ichatbasecomponent) {
150 147 + disconnect(CraftChatMessage.fromComponent(ichatbasecomponent));
151 148 + }
152 149 + // CraftBukkit end
153 150 +
181 178 MinecraftServer minecraftserver = this.server;
182 179 NetworkManager networkmanager = this.connection;
183 180
184 181 Objects.requireNonNull(this.connection);
185 182 - minecraftserver.executeBlocking(networkmanager::handleDisconnection);
186 183 + // CraftBukkit - Don't wait
187 184 + minecraftserver.wrapRunnable(networkmanager::handleDisconnection);
188 185 }
189 186
190 187 private <T, R> void filterTextPacket(T t0, Consumer<R> consumer, BiFunction<ITextFilter, T, CompletableFuture<R>> bifunction) {
191 -@@ -371,7 +492,34 @@
188 +@@ -371,7 +489,34 @@
192 189 double d9 = entity.getDeltaMovement().lengthSqr();
193 190 double d10 = d6 * d6 + d7 * d7 + d8 * d8;
194 191
195 192 - if (d10 - d9 > 100.0D && !this.isSingleplayerOwner()) {
196 193 +
197 194 + // CraftBukkit start - handle custom speeds and skipped ticks
198 195 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
199 196 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
200 197 + this.lastTick = (int) (System.currentTimeMillis() / 50);
201 198 +
217 214 + } else {
218 215 + speed = player.getAbilities().walkingSpeed * 10f;
219 216 + }
220 217 + speed *= 2f; // TODO: Get the speed of the vehicle instead of the player
221 218 +
222 219 + if (d10 - d9 > Math.max(100.0D, Math.pow((double) (10.0F * (float) i * speed), 2)) && !this.isSingleplayerOwner()) {
223 220 + // CraftBukkit end
224 221 PlayerConnection.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", entity.getName().getString(), this.player.getName().getString(), d6, d7, d8);
225 222 this.connection.send(new PacketPlayOutVehicleMove(entity));
226 223 return;
227 -@@ -401,14 +549,72 @@
224 +@@ -401,14 +546,72 @@
228 225 }
229 226
230 227 entity.absMoveTo(d3, d4, d5, f, f1);
231 228 + player.absMoveTo(d3, d4, d5, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
232 229 boolean flag2 = worldserver.noCollision(entity, entity.getBoundingBox().deflate(0.0625D));
233 230
234 231 if (flag && (flag1 || !flag2)) {
235 232 entity.absMoveTo(d0, d1, d2, f, f1);
236 233 + player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit
237 234 this.connection.send(new PacketPlayOutVehicleMove(entity));
290 287 + this.justTeleported = false;
291 288 + return;
292 289 + }
293 290 + }
294 291 + }
295 292 + // CraftBukkit end
296 293 +
297 294 this.player.getLevel().getChunkSource().move(this.player);
298 295 this.player.checkMovementStatistics(this.player.getX() - d0, this.player.getY() - d1, this.player.getZ() - d2);
299 296 this.clientVehicleIsFloating = d11 >= -0.03125D && !this.server.isFlightAllowed() && this.noBlocksAround(entity);
300 -@@ -427,7 +633,7 @@
297 +@@ -427,7 +630,7 @@
301 298 @Override
302 299 public void handleAcceptTeleportPacket(PacketPlayInTeleportAccept packetplayinteleportaccept) {
303 300 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinteleportaccept, this, this.player.getLevel());
304 301 - if (packetplayinteleportaccept.getId() == this.awaitingTeleport) {
305 302 + if (packetplayinteleportaccept.getId() == this.awaitingTeleport && this.awaitingPositionFromClient != null) { // CraftBukkit
306 303 this.player.absMoveTo(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
307 304 this.lastGoodX = this.awaitingPositionFromClient.x;
308 305 this.lastGoodY = this.awaitingPositionFromClient.y;
309 -@@ -437,6 +643,7 @@
306 +@@ -437,6 +640,7 @@
310 307 }
311 308
312 309 this.awaitingPositionFromClient = null;
313 310 + this.player.getLevel().getChunkSource().move(this.player); // CraftBukkit
314 311 }
315 312
316 313 }
317 -@@ -444,7 +651,7 @@
314 +@@ -444,7 +648,7 @@
318 315 @Override
319 316 public void handleRecipeBookSeenRecipePacket(PacketPlayInRecipeDisplayed packetplayinrecipedisplayed) {
320 317 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinrecipedisplayed, this, this.player.getLevel());
321 318 - Optional optional = this.server.getRecipeManager().byKey(packetplayinrecipedisplayed.getRecipe());
322 319 + Optional<? extends IRecipe<?>> optional = this.server.getRecipeManager().byKey(packetplayinrecipedisplayed.getRecipe()); // CraftBukkit - decompile error
323 320 RecipeBookServer recipebookserver = this.player.getRecipeBook();
324 321
325 322 Objects.requireNonNull(recipebookserver);
326 -@@ -474,6 +681,12 @@
323 +@@ -474,6 +678,12 @@
327 324 @Override
328 325 public void handleCustomCommandSuggestions(PacketPlayInTabComplete packetplayintabcomplete) {
329 326 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayintabcomplete, this, this.player.getLevel());
330 327 + // CraftBukkit start
331 328 + if (chatSpamTickCount.addAndGet(1) > 500 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
332 329 + this.disconnect(new ChatMessage("disconnect.spam", new Object[0]));
333 330 + return;
334 331 + }
335 332 + // CraftBukkit end
336 333 StringReader stringreader = new StringReader(packetplayintabcomplete.getCommand());
337 334
338 335 if (stringreader.canRead() && stringreader.peek() == '/') {
339 -@@ -483,6 +696,7 @@
336 +@@ -483,6 +693,7 @@
340 337 ParseResults<CommandListenerWrapper> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
341 338
342 339 this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
343 340 + if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
344 341 this.connection.send(new PacketPlayOutTabComplete(packetplayintabcomplete.getId(), suggestions));
345 342 });
346 343 }
347 -@@ -715,6 +929,13 @@
344 +@@ -715,6 +926,13 @@
348 345
349 346 if (container instanceof ContainerMerchant) {
350 347 ContainerMerchant containermerchant = (ContainerMerchant) container;
351 348 + // CraftBukkit start
352 349 + final org.bukkit.event.inventory.TradeSelectEvent tradeSelectEvent = CraftEventFactory.callTradeSelectEvent(this.player, i, containermerchant);
353 350 + if (tradeSelectEvent.isCancelled()) {
354 351 + this.player.getBukkitEntity().updateInventory();
355 352 + return;
356 353 + }
357 354 + // CraftBukkit end
358 355
359 356 containermerchant.setSelectionHint(i);
360 357 containermerchant.tryMoveItems(i);
361 -@@ -724,6 +945,13 @@
358 +@@ -724,6 +942,13 @@
362 359
363 360 @Override
364 361 public void handleEditBook(PacketPlayInBEdit packetplayinbedit) {
365 362 + // CraftBukkit start
366 363 + if (this.lastBookTick + 20 > MinecraftServer.currentTick) {
367 364 + this.disconnect("Book edited too quickly!");
368 365 + return;
369 366 + }
370 367 + this.lastBookTick = MinecraftServer.currentTick;
371 368 + // CraftBukkit end
372 369 int i = packetplayinbedit.getSlot();
373 370
374 371 if (PlayerInventory.isHotbarSlot(i) || i == 40) {
375 -@@ -732,7 +960,7 @@
372 +@@ -732,7 +957,7 @@
376 373
377 374 Objects.requireNonNull(list);
378 375 optional.ifPresent(list::add);
379 376 - Stream stream = packetplayinbedit.getPages().stream().limit(100L);
380 377 + Stream<String> stream = packetplayinbedit.getPages().stream().limit(100L); // CraftBukkit - decompile error
381 378
382 379 Objects.requireNonNull(list);
383 380 stream.forEach(list::add);
384 -@@ -748,7 +976,7 @@
381 +@@ -748,7 +973,7 @@
385 382 ItemStack itemstack = this.player.getInventory().getItem(i);
386 383
387 384 if (itemstack.is(Items.WRITABLE_BOOK)) {
388 385 - this.updateBookPages(list, UnaryOperator.identity(), itemstack);
389 386 + this.updateBookPages(list, UnaryOperator.identity(), itemstack.copy(), i, itemstack); // CraftBukkit
390 387 }
391 388 }
392 389
393 -@@ -773,16 +1001,16 @@
390 +@@ -773,16 +998,16 @@
394 391
395 392 this.updateBookPages(list, (s) -> {
396 393 return IChatBaseComponent.ChatSerializer.toJson(new ChatComponentText(s));
397 394 - }, itemstack1);
398 395 - this.player.getInventory().setItem(i, itemstack1);
399 396 + }, itemstack1, i, itemstack); // CraftBukkit
400 397 + this.player.getInventory().setItem(i, itemstack); // CraftBukkit - event factory updates the hand book
401 398 }
402 399 }
403 400
404 401 - private void updateBookPages(List<ITextFilter.a> list, UnaryOperator<String> unaryoperator, ItemStack itemstack) {
405 402 + private void updateBookPages(List<ITextFilter.a> list, UnaryOperator<String> unaryoperator, ItemStack itemstack, int slot, ItemStack handItem) { // CraftBukkit
406 403 NBTTagList nbttaglist = new NBTTagList();
407 404
408 405 if (this.player.isTextFilteringEnabled()) {
409 406 - Stream stream = list.stream().map((itextfilter_a) -> {
410 407 + Stream<NBTTagString> stream = list.stream().map((itextfilter_a) -> { // CraftBukkit - decompile error
411 408 return NBTTagString.valueOf((String) unaryoperator.apply(itextfilter_a.getFiltered()));
412 409 });
413 410
414 -@@ -810,6 +1038,7 @@
411 +@@ -810,6 +1035,7 @@
415 412 }
416 413
417 414 itemstack.addTagElement("pages", nbttaglist);
418 415 + CraftEventFactory.handleEditBookEvent(player, slot, handItem, itemstack); // CraftBukkit
419 416 }
420 417
421 418 @Override
422 -@@ -846,7 +1075,7 @@
419 +@@ -846,7 +1072,7 @@
423 420 } else {
424 421 WorldServer worldserver = this.player.getLevel();
425 422
426 423 - if (!this.player.wonGame) {
427 424 + if (!this.player.wonGame && !this.player.isImmobile()) { // CraftBukkit
428 425 if (this.tickCount == 0) {
429 426 this.resetPosition();
430 427 }
431 -@@ -856,7 +1085,7 @@
428 +@@ -856,7 +1082,7 @@
432 429 this.awaitingTeleportTime = this.tickCount;
433 430 this.teleport(this.awaitingPositionFromClient.x, this.awaitingPositionFromClient.y, this.awaitingPositionFromClient.z, this.player.getYRot(), this.player.getXRot());
434 431 }
435 432 -
436 433 + this.allowedPlayerTicks = 20; // CraftBukkit
437 434 } else {
438 435 this.awaitingTeleportTime = this.tickCount;
439 436 double d0 = clampHorizontal(packetplayinflying.getX(this.player.getX()));
440 -@@ -868,7 +1097,15 @@
437 +@@ -868,7 +1094,15 @@
441 438 if (this.player.isPassenger()) {
442 439 this.player.absMoveTo(this.player.getX(), this.player.getY(), this.player.getZ(), f, f1);
443 440 this.player.getLevel().getChunkSource().move(this.player);
444 441 + this.allowedPlayerTicks = 20; // CraftBukkit
445 442 } else {
446 443 + // CraftBukkit - Make sure the move is valid but then reset it for plugins to modify
447 444 + double prevX = player.getX();
448 445 + double prevY = player.getY();
449 446 + double prevZ = player.getZ();
450 447 + float prevYaw = player.getYRot();
451 448 + float prevPitch = player.getXRot();
452 449 + // CraftBukkit end
453 450 double d3 = this.player.getX();
454 451 double d4 = this.player.getY();
455 452 double d5 = this.player.getZ();
456 -@@ -888,15 +1125,33 @@
453 +@@ -888,15 +1122,33 @@
457 454 ++this.receivedMovePacketCount;
458 455 int i = this.receivedMovePacketCount - this.knownMovePacketCount;
459 456
460 457 - if (i > 5) {
461 458 + // CraftBukkit start - handle custom speeds and skipped ticks
462 459 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
463 460 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
464 461 + this.lastTick = (int) (System.currentTimeMillis() / 50);
465 462 +
466 463 + if (i > Math.max(this.allowedPlayerTicks, 5)) {
482 479 +
483 480 if (!this.player.isChangingDimension() && (!this.player.getLevel().getGameRules().getBoolean(GameRules.RULE_DISABLE_ELYTRA_MOVEMENT_CHECK) || !this.player.isFallFlying())) {
484 481 float f2 = this.player.isFallFlying() ? 300.0F : 100.0F;
485 482
486 483 - if (d11 - d10 > (double) (f2 * (float) i) && !this.isSingleplayerOwner()) {
487 484 + if (d11 - d10 > Math.max(f2, Math.pow((double) (10.0F * (float) i * speed), 2)) && !this.isSingleplayerOwner()) {
488 485 + // CraftBukkit end
489 486 PlayerConnection.LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getName().getString(), d7, d8, d9);
490 487 this.teleport(this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYRot(), this.player.getXRot());
491 488 return;
492 -@@ -915,6 +1170,7 @@
489 +@@ -915,6 +1167,7 @@
493 490 }
494 491
495 492 this.player.move(EnumMoveType.PLAYER, new Vec3D(d7, d8, d9));
496 493 + this.player.onGround = packetplayinflying.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
497 494 double d12 = d8;
498 495
499 496 d7 = d0 - this.player.getX();
500 -@@ -936,7 +1192,71 @@
497 +@@ -936,7 +1189,71 @@
501 498 if (!this.player.noPhysics && !this.player.isSleeping() && (flag1 && worldserver.noCollision(this.player, axisalignedbb) || this.isPlayerCollidingWithAnythingNew(worldserver, axisalignedbb))) {
502 499 this.teleport(d3, d4, d5, f, f1);
503 500 } else {
504 501 - this.clientIsFloating = d12 >= -0.03125D && this.player.gameMode.getGameModeForPlayer() != EnumGamemode.SPECTATOR && !this.server.isFlightAllowed() && !this.player.getAbilities().mayfly && !this.player.hasEffect(MobEffects.LEVITATION) && !this.player.isFallFlying() && this.noBlocksAround(this.player);
505 502 + // CraftBukkit start - fire PlayerMoveEvent
506 503 + // Rest to old location first
507 504 + this.player.absMoveTo(prevX, prevY, prevZ, prevYaw, prevPitch);
508 505 +
509 506 + Player player = this.getCraftPlayer();
510 507 + Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location.
563 560 + }
564 561 + }
565 562 + this.player.absMoveTo(d0, d1, d2, f, f1); // Copied from above
566 563 +
567 564 + // MC-135989, SPIGOT-5564: isRiptiding
568 565 + this.clientIsFloating = d12 >= -0.03125D && this.player.gameMode.getGameModeForPlayer() != EnumGamemode.SPECTATOR && !this.server.isFlightAllowed() && !this.player.getAbilities().mayfly && !this.player.hasEffect(MobEffects.LEVITATION) && !this.player.isFallFlying() && this.noBlocksAround(this.player) && !this.player.isAutoSpinAttack();
569 566 + // CraftBukkit end
570 567 this.player.getLevel().getChunkSource().move(this.player);
571 568 this.player.doCheckFallDamage(this.player.getY() - d6, packetplayinflying.isOnGround());
572 569 this.player.setOnGround(packetplayinflying.isOnGround());
573 -@@ -974,19 +1294,80 @@
570 +@@ -974,19 +1291,80 @@
574 571 return true;
575 572 }
576 573
577 574 + // CraftBukkit start - Delegate to teleport(Location)
578 575 public void dismount(double d0, double d1, double d2, float f, float f1) {
579 576 - this.teleport(d0, d1, d2, f, f1, Collections.emptySet(), true);
580 577 + this.dismount(d0, d1, d2, f, f1, PlayerTeleportEvent.TeleportCause.UNKNOWN);
581 578 + }
582 579 +
583 580 + public void dismount(double d0, double d1, double d2, float f, float f1, PlayerTeleportEvent.TeleportCause cause) {
648 645 + }
649 646 + if (Float.isNaN(f1)) {
650 647 + f1 = 0;
651 648 + }
652 649 +
653 650 + this.justTeleported = true;
654 651 + // CraftBukkit end
655 652 double d3 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.X) ? this.player.getX() : 0.0D;
656 653 double d4 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y) ? this.player.getY() : 0.0D;
657 654 double d5 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Z) ? this.player.getZ() : 0.0D;
658 -@@ -998,6 +1379,14 @@
655 +@@ -998,6 +1376,14 @@
659 656 this.awaitingTeleport = 0;
660 657 }
661 658
662 659 + // CraftBukkit start - update last location
663 660 + this.lastPosX = this.awaitingPositionFromClient.x;
664 661 + this.lastPosY = this.awaitingPositionFromClient.y;
665 662 + this.lastPosZ = this.awaitingPositionFromClient.z;
666 663 + this.lastYaw = f;
667 664 + this.lastPitch = f1;
668 665 + // CraftBukkit end
669 666 +
670 667 this.awaitingTeleportTime = this.tickCount;
671 668 this.player.absMoveTo(d0, d1, d2, f, f1);
672 669 this.player.connection.send(new PacketPlayOutPosition(d0 - d3, d1 - d4, d2 - d5, f - f2, f1 - f3, set, this.awaitingTeleport, flag));
673 -@@ -1006,6 +1395,7 @@
670 +@@ -1006,6 +1392,7 @@
674 671 @Override
675 672 public void handlePlayerAction(PacketPlayInBlockDig packetplayinblockdig) {
676 673 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinblockdig, this, this.player.getLevel());
677 674 + if (this.player.isImmobile()) return; // CraftBukkit
678 675 BlockPosition blockposition = packetplayinblockdig.getPos();
679 676
680 677 this.player.resetLastActionTime();
681 -@@ -1016,14 +1406,46 @@
678 +@@ -1016,14 +1403,46 @@
682 679 if (!this.player.isSpectator()) {
683 680 ItemStack itemstack = this.player.getItemInHand(EnumHand.OFF_HAND);
684 681
685 682 - this.player.setItemInHand(EnumHand.OFF_HAND, this.player.getItemInHand(EnumHand.MAIN_HAND));
686 683 - this.player.setItemInHand(EnumHand.MAIN_HAND, itemstack);
687 684 + // CraftBukkit start - inspiration taken from DispenserRegistry (See SpigotCraft#394)
688 685 + CraftItemStack mainHand = CraftItemStack.asCraftMirror(itemstack);
689 686 + CraftItemStack offHand = CraftItemStack.asCraftMirror(this.player.getItemInHand(EnumHand.MAIN_HAND));
690 687 + PlayerSwapHandItemsEvent swapItemsEvent = new PlayerSwapHandItemsEvent(getCraftPlayer(), mainHand.clone(), offHand.clone());
691 688 + this.cserver.getPluginManager().callEvent(swapItemsEvent);
720 717 + if (this.dropCount >= 20) {
721 718 + LOGGER.warn(this.player.getScoreboardName() + " dropped their items too quickly!");
722 719 + this.disconnect("You dropped your items too quickly (Hacking?)");
723 720 + return;
724 721 + }
725 722 + }
726 723 + // CraftBukkit end
727 724 this.player.drop(false);
728 725 }
729 726
730 -@@ -1060,6 +1482,7 @@
727 +@@ -1060,6 +1479,7 @@
731 728 @Override
732 729 public void handleUseItemOn(PacketPlayInUseItem packetplayinuseitem) {
733 730 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinuseitem, this, this.player.getLevel());
734 731 + if (this.player.isImmobile()) return; // CraftBukkit
735 732 WorldServer worldserver = this.player.getLevel();
736 733 EnumHand enumhand = packetplayinuseitem.getHand();
737 734 ItemStack itemstack = this.player.getItemInHand(enumhand);
738 -@@ -1072,6 +1495,14 @@
735 +@@ -1069,9 +1489,16 @@
736 +
737 + this.player.resetLastActionTime();
738 + int i = this.player.level.getMaxBuildHeight();
739 ++ // CraftBukkit start
740 ++ double distanceSqr = this.player.distanceToSqr((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D);
741 ++ if (distanceSqr > 100.0D) {
742 ++ return;
743 ++ }
744 ++ // CraftBukkit end
739 745
740 746 if (blockposition.getY() < i) {
741 - 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)) {
742 -+ // CraftBukkit start - Check if we can actually do something over this large a distance
743 -+ Location eyeLoc = this.getCraftPlayer().getEyeLocation();
744 -+ double reachDistance = NumberConversions.square(eyeLoc.getX() - blockposition.getX()) + NumberConversions.square(eyeLoc.getY() - blockposition.getY()) + NumberConversions.square(eyeLoc.getZ() - blockposition.getZ());
745 -+ if (reachDistance > (this.getCraftPlayer().getGameMode() == org.bukkit.GameMode.CREATIVE ? CREATIVE_PLACE_DISTANCE_SQUARED : SURVIVAL_PLACE_DISTANCE_SQUARED)) {
746 -+ return;
747 -+ }
748 -+ this.player.stopUsingItem(); // SPIGOT-4706
749 -+ // CraftBukkit end
747 +- 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)) {
748 ++ if (this.awaitingPositionFromClient == null && distanceSqr < 64.0D && worldserver.mayInteract(this.player, blockposition)) { // CraftBukkit - reuse value
749 ++ this.player.stopUsingItem(); // CraftBukkit - SPIGOT-4706
750 750 EnumInteractionResult enuminteractionresult = this.player.gameMode.useItemOn(this.player, worldserver, itemstack, enumhand, movingobjectpositionblock);
751 751
752 752 if (enumdirection == EnumDirection.UP && !enuminteractionresult.consumesAction() && blockposition.getY() >= i - 1 && wasBlockPlacementAttempt(this.player, itemstack)) {
753 -@@ -1095,12 +1526,51 @@
753 +@@ -1095,12 +1522,51 @@
754 754 @Override
755 755 public void handleUseItem(PacketPlayInBlockPlace packetplayinblockplace) {
756 756 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinblockplace, this, this.player.getLevel());
757 757 + if (this.player.isImmobile()) return; // CraftBukkit
758 758 WorldServer worldserver = this.player.getLevel();
759 759 EnumHand enumhand = packetplayinblockplace.getHand();
760 760 ItemStack itemstack = this.player.getItemInHand(enumhand);
761 761
762 762 this.player.resetLastActionTime();
763 763 if (!itemstack.isEmpty()) {
795 795 + player.gameMode.firedInteract = false;
796 796 + }
797 797 +
798 798 + if (cancelled) {
799 799 + this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524
800 800 + return;
801 801 + }
802 802 EnumInteractionResult enuminteractionresult = this.player.gameMode.useItem(this.player, worldserver, itemstack, enumhand);
803 803
804 804 if (enuminteractionresult.shouldSwing()) {
805 -@@ -1121,7 +1591,7 @@
805 +@@ -1121,7 +1587,7 @@
806 806 Entity entity = packetplayinspectate.getEntity(worldserver);
807 807
808 808 if (entity != null) {
809 809 - this.player.teleportTo(worldserver, entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot());
810 810 + this.player.teleportTo(worldserver, entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot(), org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.SPECTATE); // CraftBukkit
811 811 return;
812 812 }
813 813 }
814 -@@ -1136,6 +1606,7 @@
814 +@@ -1136,6 +1602,7 @@
815 815 PlayerConnection.LOGGER.info("Disconnecting {} due to resource pack rejection", this.player.getName());
816 816 this.disconnect(new ChatMessage("multiplayer.requiredTexturePrompt.disconnect"));
817 817 }
818 818 + this.cserver.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(getCraftPlayer(), PlayerResourcePackStatusEvent.Status.values()[packetplayinresourcepackstatus.action.ordinal()])); // CraftBukkit
819 819
820 820 }
821 821
822 -@@ -1155,11 +1626,26 @@
822 +@@ -1155,11 +1622,26 @@
823 823
824 824 @Override
825 825 public void onDisconnect(IChatBaseComponent ichatbasecomponent) {
826 826 + // CraftBukkit start - Rarely it would send a disconnect line twice
827 827 + if (this.processedDisconnect) {
828 828 + return;
829 829 + } else {
830 830 + this.processedDisconnect = true;
831 831 + }
832 832 + // CraftBukkit end
840 840 this.player.disconnect();
841 841 - this.server.getPlayerList().remove(this.player);
842 842 + String quitMessage = this.server.getPlayerList().remove(this.player);
843 843 + if ((quitMessage != null) && (quitMessage.length() > 0)) {
844 844 + this.server.getPlayerList().broadcastMessage(CraftChatMessage.fromString(quitMessage));
845 845 + }
846 846 + // CraftBukkit end
847 847 this.player.getTextFilter().leave();
848 848 if (this.isSingleplayerOwner()) {
849 849 PlayerConnection.LOGGER.info("Stopping singleplayer server as player logged out");
850 -@@ -1174,6 +1660,15 @@
850 +@@ -1174,6 +1656,15 @@
851 851 }
852 852
853 853 public void send(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
854 854 + // CraftBukkit start
855 855 + if (packet == null) {
856 856 + return;
857 857 + } else if (packet instanceof PacketPlayOutSpawnPosition) {
858 858 + PacketPlayOutSpawnPosition packet6 = (PacketPlayOutSpawnPosition) packet;
859 859 + this.player.compassTarget = new Location(this.getCraftPlayer().getWorld(), packet6.pos.getX(), packet6.pos.getY(), packet6.pos.getZ());
860 860 + }
861 861 + // CraftBukkit end
862 862 +
863 863 try {
864 864 this.connection.send(packet, genericfuturelistener);
865 865 } catch (Throwable throwable) {
866 -@@ -1190,7 +1685,16 @@
866 +@@ -1190,7 +1681,16 @@
867 867 @Override
868 868 public void handleSetCarriedItem(PacketPlayInHeldItemSlot packetplayinhelditemslot) {
869 869 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinhelditemslot, this, this.player.getLevel());
870 870 + if (this.player.isImmobile()) return; // CraftBukkit
871 871 if (packetplayinhelditemslot.getSlot() >= 0 && packetplayinhelditemslot.getSlot() < PlayerInventory.getSelectionSize()) {
872 872 + PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getCraftPlayer(), this.player.getInventory().selected, packetplayinhelditemslot.getSlot());
873 873 + this.cserver.getPluginManager().callEvent(event);
874 874 + if (event.isCancelled()) {
875 875 + this.send(new PacketPlayOutHeldItemSlot(this.player.getInventory().selected));
876 876 + this.player.resetLastActionTime();
877 877 + return;
878 878 + }
879 879 + // CraftBukkit end
880 880 if (this.player.getInventory().selected != packetplayinhelditemslot.getSlot() && this.player.getUsedItemHand() == EnumHand.MAIN_HAND) {
881 881 this.player.stopUsingItem();
882 882 }
883 -@@ -1199,11 +1703,18 @@
883 +@@ -1199,11 +1699,18 @@
884 884 this.player.resetLastActionTime();
885 885 } else {
886 886 PlayerConnection.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString());
887 887 + this.disconnect("Invalid hotbar selection (Hacking?)"); // CraftBukkit
888 888 }
889 889 }
890 890
891 891 @Override
892 892 public void handleChat(PacketPlayInChat packetplayinchat) {
893 893 + // CraftBukkit start - async chat
894 894 + // SPIGOT-3638
895 895 + if (this.server.isStopped()) {
896 896 + return;
897 897 + }
898 898 + // CraftBukkit end
899 899 String s = StringUtils.normalizeSpace(packetplayinchat.getMessage());
900 900
901 901 for (int i = 0; i < s.length(); ++i) {
902 -@@ -1217,20 +1728,42 @@
902 +@@ -1217,20 +1724,42 @@
903 903 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinchat, this, this.player.getLevel());
904 904 this.handleChat(ITextFilter.a.passThrough(s));
905 905 } else {
906 906 - this.filterTextPacket(s, this::handleChat);
907 907 + this.handleChat(ITextFilter.a.passThrough(s)); // CraftBukkit - filter NYI
908 908 }
909 909
910 910 }
911 911
912 912 private void handleChat(ITextFilter.a itextfilter_a) {
939 939 + }
940 940 + });
941 941 + } else if (this.player.getChatVisibility() == EnumChatVisibility.SYSTEM) { // Re-add "Command Only" flag check
942 942 + this.send(new PacketPlayOutChat((new ChatMessage("chat.cannotSend")).withStyle(EnumChatFormat.RED), ChatMessageType.SYSTEM, SystemUtils.NIL_UUID));
943 943 + } else if (true) {
944 944 + this.chat(s, true);
945 945 + // CraftBukkit end - the below is for reference. :)
946 946 } else {
947 947 String s1 = itextfilter_a.getFiltered();
948 948 ChatMessage chatmessage = s1.isEmpty() ? null : new ChatMessage("chat.type.text", new Object[]{this.player.getDisplayName(), s1});
949 -@@ -1241,28 +1774,198 @@
949 +@@ -1241,28 +1770,198 @@
950 950 }, ChatMessageType.CHAT, this.player.getUUID());
951 951 }
952 952
953 953 - this.chatSpamTickCount += 20;
954 954 - if (this.chatSpamTickCount > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
955 955 - this.disconnect(new ChatMessage("disconnect.spam"));
956 956 + // CraftBukkit start - replaced with thread safe throttle
957 957 + // this.chatSpamTickCount += 20;
958 958 + if (chatSpamTickCount.addAndGet(20) > 200 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
959 959 + if (!isSync) {
1142 1142 +
1143 1143 + if (e2.isCancelled()) {
1144 1144 + return;
1145 1145 + }
1146 1146 + break;
1147 1147 + }
1148 1148 + // CraftBukkit end
1149 1149 this.player.resetLastActionTime();
1150 1150 IJumpable ijumpable;
1151 1151
1152 -@@ -1320,6 +2023,7 @@
1152 +@@ -1320,6 +2019,7 @@
1153 1153 @Override
1154 1154 public void handleInteract(PacketPlayInUseEntity packetplayinuseentity) {
1155 1155 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinuseentity, this, this.player.getLevel());
1156 1156 + if (this.player.isImmobile()) return; // CraftBukkit
1157 1157 WorldServer worldserver = this.player.getLevel();
1158 1158 final Entity entity = packetplayinuseentity.getTarget(worldserver);
1159 1159
1160 -@@ -1334,10 +2038,44 @@
1160 +@@ -1334,10 +2034,44 @@
1161 1161
1162 1162 if (this.player.distanceToSqr(entity) < 36.0D) {
1163 1163 packetplayinuseentity.dispatch(new PacketPlayInUseEntity.c() {
1164 1164 - private void performInteraction(EnumHand enumhand, PlayerConnection.a playerconnection_a) {
1165 1165 + private void performInteraction(EnumHand enumhand, PlayerConnection.a playerconnection_a, PlayerInteractEntityEvent event) { // CraftBukkit
1166 1166 ItemStack itemstack = PlayerConnection.this.player.getItemInHand(enumhand).copy();
1167 1167 + // CraftBukkit start
1168 1168 + ItemStack itemInHand = PlayerConnection.this.player.getItemInHand(enumhand);
1169 1169 + boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEAD && entity instanceof EntityInsentient;
1170 1170 + Item origItem = player.getInventory().getSelected() == null ? null : player.getInventory().getSelected().getItem();
1196 1196
1197 1197 + // CraftBukkit start
1198 1198 + if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) {
1199 1199 + player.containerMenu.sendAllDataToRemote();
1200 1200 + }
1201 1201 + // CraftBukkit end
1202 1202 +
1203 1203 if (enuminteractionresult.consumesAction()) {
1204 1204 CriterionTriggers.PLAYER_INTERACTED_WITH_ENTITY.trigger(PlayerConnection.this.player, itemstack, entity);
1205 1205 if (enuminteractionresult.shouldSwing()) {
1206 -@@ -1349,20 +2087,27 @@
1206 +@@ -1349,20 +2083,27 @@
1207 1207
1208 1208 @Override
1209 1209 public void onInteraction(EnumHand enumhand) {
1210 1210 - this.performInteraction(enumhand, EntityHuman::interactOn);
1211 1211 + this.performInteraction(enumhand, EntityHuman::interactOn, new PlayerInteractEntityEvent(getCraftPlayer(), entity.getBukkitEntity(), (enumhand == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND));
1212 1212 }
1213 1213
1214 1214 @Override
1215 1215 public void onInteraction(EnumHand enumhand, Vec3D vec3d) {
1216 1216 this.performInteraction(enumhand, (entityplayer, entity1, enumhand1) -> {
1227 1227 + ItemStack itemInHand = PlayerConnection.this.player.getMainHandItem();
1228 1228 PlayerConnection.this.player.attack(entity);
1229 1229 +
1230 1230 + if (!itemInHand.isEmpty() && itemInHand.getCount() <= -1) {
1231 1231 + player.containerMenu.sendAllDataToRemote();
1232 1232 + }
1233 1233 + // CraftBukkit end
1234 1234 } else {
1235 1235 PlayerConnection.this.disconnect(new ChatMessage("multiplayer.disconnect.invalid_entity_attacked"));
1236 1236 PlayerConnection.LOGGER.warn("Player {} tried to attack an invalid entity", PlayerConnection.this.player.getName().getString());
1237 -@@ -1407,21 +2152,304 @@
1237 +@@ -1407,21 +2148,304 @@
1238 1238 @Override
1239 1239 public void handleContainerClose(PacketPlayInCloseWindow packetplayinclosewindow) {
1240 1240 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinclosewindow, this, this.player.getLevel());
1241 1241 +
1242 1242 + if (this.player.isImmobile()) return; // CraftBukkit
1243 1243 + CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit
1244 1244 +
1245 1245 this.player.doCloseContainer();
1246 1246 }
1247 1247
1535 1535 + if (event instanceof CraftItemEvent || event instanceof SmithItemEvent) {
1536 1536 + // Need to update the inventory on crafting to
1537 1537 + // correctly support custom recipes
1538 1538 + player.containerMenu.sendAllDataToRemote();
1539 1539 + }
1540 1540 + }
1541 1541 + // CraftBukkit end
1542 1542 ObjectIterator objectiterator = Int2ObjectMaps.fastIterable(packetplayinwindowclick.getChangedSlots()).iterator();
1543 1543
1544 1544 while (objectiterator.hasNext()) {
1545 -@@ -1456,6 +2484,7 @@
1545 +@@ -1456,6 +2480,7 @@
1546 1546 @Override
1547 1547 public void handleContainerButtonClick(PacketPlayInEnchantItem packetplayinenchantitem) {
1548 1548 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinenchantitem, this, this.player.getLevel());
1549 1549 + if (this.player.isImmobile()) return; // CraftBukkit
1550 1550 this.player.resetLastActionTime();
1551 1551 if (this.player.containerMenu.containerId == packetplayinenchantitem.getContainerId() && !this.player.isSpectator()) {
1552 1552 this.player.containerMenu.clickMenuButton(this.player, packetplayinenchantitem.getButtonId());
1553 -@@ -1483,6 +2512,43 @@
1553 +@@ -1483,6 +2508,43 @@
1554 1554
1555 1555 boolean flag1 = packetplayinsetcreativeslot.getSlotNum() >= 1 && packetplayinsetcreativeslot.getSlotNum() <= 45;
1556 1556 boolean flag2 = itemstack.isEmpty() || itemstack.getDamageValue() >= 0 && itemstack.getCount() <= 64 && !itemstack.isEmpty();
1557 1557 + if (flag || (flag1 && !ItemStack.matches(this.player.inventoryMenu.getSlot(packetplayinsetcreativeslot.getSlotNum()).getItem(), packetplayinsetcreativeslot.getItem()))) { // Insist on valid slot
1558 1558 + // CraftBukkit start - Call click event
1559 1559 + InventoryView inventory = this.player.inventoryMenu.getBukkitView();
1560 1560 + org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packetplayinsetcreativeslot.getItem());
1561 1561 +
1562 1562 + SlotType type = SlotType.QUICKBAR;
1563 1563 + if (flag) {
1587 1587 + this.player.connection.send(new PacketPlayOutSetSlot(this.player.inventoryMenu.containerId, this.player.inventoryMenu.incrementStateId(), packetplayinsetcreativeslot.getSlotNum(), this.player.inventoryMenu.getSlot(packetplayinsetcreativeslot.getSlotNum()).getItem()));
1588 1588 + this.player.connection.send(new PacketPlayOutSetSlot(-1, this.player.inventoryMenu.incrementStateId(), -1, ItemStack.EMPTY));
1589 1589 + }
1590 1590 + return;
1591 1591 + }
1592 1592 + }
1593 1593 + // CraftBukkit end
1594 1594
1595 1595 if (flag1 && flag2) {
1596 1596 this.player.inventoryMenu.getSlot(packetplayinsetcreativeslot.getSlotNum()).set(itemstack);
1597 -@@ -1505,6 +2571,7 @@
1597 +@@ -1505,6 +2567,7 @@
1598 1598 }
1599 1599
1600 1600 private void updateSignText(PacketPlayInUpdateSign packetplayinupdatesign, List<ITextFilter.a> list) {
1601 1601 + if (this.player.isImmobile()) return; // CraftBukkit
1602 1602 this.player.resetLastActionTime();
1603 1603 WorldServer worldserver = this.player.getLevel();
1604 1604 BlockPosition blockposition = packetplayinupdatesign.getPos();
1605 -@@ -1521,18 +2588,37 @@
1605 +@@ -1521,18 +2584,37 @@
1606 1606
1607 1607 if (!tileentitysign.isEditable() || !this.player.getUUID().equals(tileentitysign.getPlayerWhoMayEdit())) {
1608 1608 PlayerConnection.LOGGER.warn("Player {} just tried to change non-editable sign", this.player.getName().getString());
1609 1609 + this.send(tileentity.getUpdatePacket()); // CraftBukkit
1610 1610 return;
1611 1611 }
1612 1612
1613 1613 + // CraftBukkit start
1614 1614 + Player player = this.player.getBukkitEntity();
1615 1615 + int x = packetplayinupdatesign.getPos().getX();
1635 1635 + IChatBaseComponent[] components = org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.getLines());
1636 1636 + for (int i = 0; i < components.length; i++) {
1637 1637 + tileentitysign.setMessage(i, components[i]);
1638 1638 }
1639 1639 + tileentitysign.isEditable = false;
1640 1640 }
1641 1641 + // CraftBukkit end
1642 1642
1643 1643 tileentitysign.setChanged();
1644 1644 worldserver.sendBlockUpdated(blockposition, iblockdata, iblockdata, 3);
1645 -@@ -1542,6 +2628,7 @@
1645 +@@ -1542,6 +2624,7 @@
1646 1646
1647 1647 @Override
1648 1648 public void handleKeepAlive(PacketPlayInKeepAlive packetplayinkeepalive) {
1649 1649 + PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinkeepalive, this, this.player.getLevel()); // CraftBukkit
1650 1650 if (this.keepAlivePending && packetplayinkeepalive.getId() == this.keepAliveChallenge) {
1651 1651 int i = (int) (SystemUtils.getMillis() - this.keepAliveTime);
1652 1652
1653 -@@ -1556,7 +2643,17 @@
1653 +@@ -1556,7 +2639,17 @@
1654 1654 @Override
1655 1655 public void handlePlayerAbilities(PacketPlayInAbilities packetplayinabilities) {
1656 1656 PlayerConnectionUtils.ensureRunningOnSameThread(packetplayinabilities, this, this.player.getLevel());
1657 1657 - this.player.getAbilities().flying = packetplayinabilities.isFlying() && this.player.getAbilities().mayfly;
1658 1658 + // CraftBukkit start
1659 1659 + if (this.player.getAbilities().mayfly && this.player.getAbilities().flying != packetplayinabilities.isFlying()) {
1660 1660 + PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.player.getBukkitEntity(), packetplayinabilities.isFlying());
1661 1661 + this.cserver.getPluginManager().callEvent(event);
1662 1662 + if (!event.isCancelled()) {
1663 1663 + this.player.getAbilities().flying = packetplayinabilities.isFlying(); // Actually set the player's flying status
1664 1664 + } else {
1665 1665 + this.player.onUpdateAbilities(); // Tell the player their ability was reverted
1666 1666 + }
1667 1667 + }
1668 1668 + // CraftBukkit end
1669 1669 }
1670 1670
1671 1671 @Override
1672 -@@ -1565,8 +2662,50 @@
1672 +@@ -1565,8 +2658,50 @@
1673 1673 this.player.updateOptions(packetplayinsettings);
1674 1674 }
1675 1675
1676 1676 - @Override
1677 1677 - public void handleCustomPayload(PacketPlayInCustomPayload packetplayincustompayload) {}
1678 1678 + // CraftBukkit start
1679 1679 + private static final MinecraftKey CUSTOM_REGISTER = new MinecraftKey("register");
1680 1680 + private static final MinecraftKey CUSTOM_UNREGISTER = new MinecraftKey("unregister");
1681 1681 +
1682 1682 + @Override

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

Add shortcut