Commits

md_5 authored 8e5e90d5eba
SPIGOT-1903: Only issue player location update after tick

Previously after a player tick, the player's location would be reset back to the location which they had prior to the tick, only to be (hopefully) corrected at a later point, after the client had attempted (and failed) to acknowledge the location change.
No tags

nms-patches/PlayerConnection.patch

Modified
63 63 private final IntHashMap<Short> k = new IntHashMap();
64 64 private double l;
65 65 @@ -51,6 +97,7 @@
66 66 private int E;
67 67 private int F;
68 68 private int G;
69 69 + private boolean processedDisconnect; // CraftBukkit - Added
70 70
71 71 public PlayerConnection(MinecraftServer minecraftserver, NetworkManager networkmanager, EntityPlayer entityplayer) {
72 72 this.minecraftServer = minecraftserver;
73 -@@ -58,7 +105,32 @@
73 +@@ -58,11 +105,38 @@
74 74 networkmanager.setPacketListener(this);
75 75 this.player = entityplayer;
76 76 entityplayer.playerConnection = this;
77 77 +
78 78 + // CraftBukkit start - add fields and methods
79 79 + this.server = minecraftserver.server;
80 80 + }
81 81 +
82 82 + private final org.bukkit.craftbukkit.CraftServer server;
83 83 + private int lastTick = MinecraftServer.currentTick;
95 95 + private float lastYaw = Float.MAX_VALUE;
96 96 + private boolean justTeleported = false;
97 97 +
98 98 + public CraftPlayer getPlayer() {
99 99 + return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity();
100 100 }
101 101 + private final static HashSet<Integer> invalidItems = new HashSet<Integer>(java.util.Arrays.asList(8, 9, 10, 11, 26, 34, 36, 43, 51, 52, 55, 59, 60, 62, 63, 64, 68, 71, 74, 75, 83, 90, 92, 93, 94, 104, 105, 115, 117, 118, 119, 125, 127, 132, 140, 141, 142, 144)); // TODO: Check after every update.
102 102 + // CraftBukkit end
103 103
104 104 public void E_() {
105 - this.d();
106 -@@ -110,15 +182,21 @@
105 +- this.d();
106 ++ // CraftBukkit start - SPIGOT-1903: reverse order of location update
107 + this.player.k_();
108 ++ this.d();
109 ++ // CraftBukkit end
110 + this.player.setLocation(this.l, this.m, this.n, this.player.yaw, this.player.pitch);
111 + ++this.e;
112 + this.G = this.F;
113 +@@ -110,15 +184,21 @@
107 114 }
108 115
109 116 this.minecraftServer.methodProfiler.b();
110 117 + // CraftBukkit start
111 118 + for (int spam; (spam = this.chatThrottle) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1); ) ;
112 119 + /* Use thread-safe field access instead
113 120 if (this.chatThrottle > 0) {
114 121 --this.chatThrottle;
115 122 }
116 123 + */
117 124 + // CraftBukkit end
118 125
119 126 if (this.j > 0) {
120 127 --this.j;
121 128 }
122 129
123 130 if (this.player.I() > 0L && this.minecraftServer.getIdleTimeout() > 0 && MinecraftServer.av() - this.player.I() > (long) (this.minecraftServer.getIdleTimeout() * 1000 * 60)) {
124 131 + this.player.resetIdleTimer(); // CraftBukkit - SPIGOT-854
125 132 this.disconnect("You have been idle for too long!");
126 133 }
127 134
128 -@@ -138,19 +216,40 @@
135 +@@ -138,19 +218,40 @@
129 136 }
130 137
131 138 public void disconnect(String s) {
132 139 + // CraftBukkit start - fire PlayerKickEvent
133 140 + if (this.processedDisconnect) {
134 141 + return;
135 142 + }
136 143 + String leaveMessage = EnumChatFormat.YELLOW + this.player.getName() + " left the game.";
137 144 +
138 145 + PlayerKickEvent event = new PlayerKickEvent(this.server.getPlayer(this.player), s, leaveMessage);
162 169 + // CraftBukkit - Don't wait
163 170 + this.minecraftServer.postToMainThread(new Runnable() {
164 171 public void run() {
165 172 PlayerConnection.this.networkManager.handleDisconnection();
166 173 }
167 174 - }));
168 175 + });
169 176 }
170 177
171 178 public void a(PacketPlayInSteerVehicle packetplayinsteervehicle) {
172 -@@ -189,7 +288,34 @@
179 +@@ -189,7 +290,34 @@
173 180 double d9 = entity.motX * entity.motX + entity.motY * entity.motY + entity.motZ * entity.motZ;
174 181 double d10 = d6 * d6 + d7 * d7 + d8 * d8;
175 182
176 183 - if (d10 - d9 > 100.0D && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(entity.getName()))) {
177 184 +
178 185 + // CraftBukkit start - handle custom speeds and skipped ticks
179 186 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
180 187 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
181 188 + this.lastTick = (int) (System.currentTimeMillis() / 50);
182 189 +
198 205 + } else {
199 206 + speed = player.abilities.walkSpeed * 10f;
200 207 + }
201 208 + speed *= 2f; // TODO: Get the speed of the vehicle instead of the player
202 209 +
203 210 + if (d10 - d9 > Math.max(100, Math.pow((double) (10.0F * (float) i * speed), 2)) && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(entity.getName()))) {
204 211 + // CraftBukkit end
205 212 PlayerConnection.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", new Object[] { entity.getName(), this.player.getName(), Double.valueOf(d6), Double.valueOf(d7), Double.valueOf(d8)});
206 213 this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity));
207 214 return;
208 -@@ -227,6 +353,62 @@
215 +@@ -227,6 +355,62 @@
209 216 return;
210 217 }
211 218
212 219 + // CraftBukkit start - fire PlayerMoveEvent
213 220 + Player player = this.getPlayer();
214 221 + Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location.
215 222 + Location to = player.getLocation().clone(); // Start off the To location as the Players current location.
216 223 +
217 224 + // If the packet contains movement information then we update the To location with the correct XYZ.
218 225 + to.setX(packetplayinvehiclemove.getX());
261 268 + this.justTeleported = false;
262 269 + return;
263 270 + }
264 271 + }
265 272 + }
266 273 + // CraftBukkit end
267 274 +
268 275 this.minecraftServer.getPlayerList().d(this.player);
269 276 this.player.checkMovement(this.player.locX - d0, this.player.locY - d1, this.player.locZ - d2);
270 277 this.D = d11 >= -0.03125D && !this.minecraftServer.getAllowFlight() && !worldserver.d(entity.getBoundingBox().g(0.0625D).a(0.0D, -0.55D, 0.0D));
271 -@@ -261,7 +443,7 @@
278 +@@ -261,7 +445,7 @@
272 279 } else {
273 280 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
274 281
275 282 - if (!this.player.viewingCredits) {
276 283 + if (!this.player.viewingCredits && !this.player.cj()) { // CraftBukkit // PAIL: rename
277 284 if (this.e == 0) {
278 285 this.d();
279 286 }
280 -@@ -271,13 +453,21 @@
287 +@@ -271,13 +455,21 @@
281 288 this.A = this.e;
282 289 this.a(this.teleportPos.x, this.teleportPos.y, this.teleportPos.z, this.player.yaw, this.player.pitch);
283 290 }
284 291 -
285 292 + this.allowedPlayerTicks = 20; // CraftBukkit
286 293 } else {
287 294 this.A = this.e;
288 295 if (this.player.isPassenger()) {
289 296 this.player.setLocation(this.player.locX, this.player.locY, this.player.locZ, packetplayinflying.a(this.player.yaw), packetplayinflying.b(this.player.pitch));
290 297 this.minecraftServer.getPlayerList().d(this.player);
293 300 + // CraftBukkit - Make sure the move is valid but then reset it for plugins to modify
294 301 + double prevX = player.locX;
295 302 + double prevY = player.locY;
296 303 + double prevZ = player.locZ;
297 304 + float prevYaw = player.yaw;
298 305 + float prevPitch = player.pitch;
299 306 + // CraftBukkit end
300 307 double d0 = this.player.locX;
301 308 double d1 = this.player.locY;
302 309 double d2 = this.player.locZ;
303 -@@ -296,15 +486,33 @@
310 +@@ -296,15 +488,33 @@
304 311 ++this.F;
305 312 int i = this.F - this.G;
306 313
307 314 - if (i > 5) {
308 315 - PlayerConnection.LOGGER.debug("{} is sending move packets too frequently ({} packets since last tick)", new Object[] { this.player.getName(), Integer.valueOf(i)});
309 316 + // CraftBukkit start - handle custom speeds and skipped ticks
310 317 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
311 318 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
312 319 + this.lastTick = (int) (System.currentTimeMillis() / 50);
313 320 +
330 337 +
331 338 if (!this.player.K() && (!this.player.x().getGameRules().getBoolean("disableElytraMovementCheck") || !this.player.cG())) {
332 339 float f2 = this.player.cG() ? 300.0F : 100.0F;
333 340
334 341 - if (d11 - d10 > (double) (f2 * (float) i) && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(this.player.getName()))) {
335 342 + if (d11 - d10 > Math.max(100, Math.pow((double) (10.0F * (float) i * speed), 2)) && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(this.player.getName()))) {
336 343 + // CraftBukkit end
337 344 PlayerConnection.LOGGER.warn("{} moved too quickly! {},{},{}", new Object[] { this.player.getName(), Double.valueOf(d7), Double.valueOf(d8), Double.valueOf(d9)});
338 345 this.a(this.player.locX, this.player.locY, this.player.locZ, this.player.yaw, this.player.pitch);
339 346 return;
340 -@@ -350,6 +558,69 @@
347 +@@ -350,6 +560,69 @@
341 348 }
342 349 }
343 350
344 351 + // CraftBukkit start - fire PlayerMoveEvent
345 352 + // Rest to old location first
346 353 + this.player.setLocation(prevX, prevY, prevZ, prevYaw, prevPitch);
347 354 +
348 355 + Player player = this.getPlayer();
349 356 + Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location.
350 357 + Location to = player.getLocation().clone(); // Start off the To location as the Players current location.
400 407 + return;
401 408 + }
402 409 + }
403 410 + }
404 411 + this.player.setLocation(d4, d5, d6, f, f1); // Copied from above
405 412 + // CraftBukkit end
406 413 +
407 414 this.B = d12 >= -0.03125D;
408 415 this.B &= !this.minecraftServer.getAllowFlight() && !this.player.abilities.canFly;
409 416 this.B &= !this.player.hasEffect(MobEffects.LEVITATION) && !this.player.cG() && !worldserver.d(this.player.getBoundingBox().g(0.0625D).a(0.0D, -0.55D, 0.0D));
410 -@@ -366,15 +637,79 @@
417 +@@ -366,15 +639,79 @@
411 418 }
412 419
413 420 public void a(double d0, double d1, double d2, float f, float f1) {
414 421 - this.a(d0, d1, d2, f, f1, Collections.emptySet());
415 422 + this.a(d0, d1, d2, f, f1, Collections.<PacketPlayOutPosition.EnumPlayerTeleportFlags>emptySet());
416 423 }
417 424
418 425 public void a(double d0, double d1, double d2, float f, float f1, Set<PacketPlayOutPosition.EnumPlayerTeleportFlags> set) {
419 426 - double d3 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.X) ? this.player.locX : 0.0D;
420 427 - double d4 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y) ? this.player.locY : 0.0D;
485 492 + this.teleportPos = this.teleportPos.add(0.0D, this.player.locY, 0.0D);
486 493 + }
487 494 +
488 495 + if (set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Z)) {
489 496 + this.teleportPos = this.teleportPos.add(0.0D, 0.0D, this.player.locZ);
490 497 + }
491 498 + // CraftBukkit end
492 499 float f2 = f;
493 500 float f3 = f1;
494 501
495 -@@ -386,6 +721,14 @@
502 +@@ -386,6 +723,14 @@
496 503 f3 = f1 + this.player.pitch;
497 504 }
498 505
499 506 + // CraftBukkit start - update last location
500 507 + this.lastPosX = this.teleportPos.x;
501 508 + this.lastPosY = this.teleportPos.y;
502 509 + this.lastPosZ = this.teleportPos.z;
503 510 + this.lastYaw = f2;
504 511 + this.lastPitch = f3;
505 512 + // CraftBukkit end
506 513 +
507 514 if (++this.teleportAwait == Integer.MAX_VALUE) {
508 515 this.teleportAwait = 0;
509 516 }
510 -@@ -397,37 +740,61 @@
517 +@@ -397,37 +742,61 @@
511 518
512 519 public void a(PacketPlayInBlockDig packetplayinblockdig) {
513 520 PlayerConnectionUtils.ensureMainThread(packetplayinblockdig, this, this.player.x());
514 521 + if (this.player.cj()) return; // CraftBukkit
515 522 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
516 523 BlockPosition blockposition = packetplayinblockdig.a();
517 524
518 525 this.player.resetIdleTimer();
519 526 ItemStack itemstack;
520 527
568 575 this.player.a(true);
569 576 }
570 577
571 578 return;
572 579
573 580 - case 4:
574 581 + case 4: // RELEASE_USE_ITEM
575 582 this.player.clearActiveItem();
576 583 itemstack = this.player.getItemInMainHand();
577 584 if (itemstack != null && itemstack.count == 0) {
578 -@@ -436,9 +803,9 @@
585 +@@ -436,9 +805,9 @@
579 586
580 587 return;
581 588
582 589 - case 5:
583 590 - case 6:
584 591 - case 7:
585 592 + case 5: // START_DESTROY_BLOCK
586 593 + case 6: // ABORT_DESTROY_BLOCK
587 594 + case 7: // STOP_DESTROY_BLOCK
588 595 double d0 = this.player.locX - ((double) blockposition.getX() + 0.5D);
589 596 double d1 = this.player.locY - ((double) blockposition.getY() + 0.5D) + 1.5D;
590 597 double d2 = this.player.locZ - ((double) blockposition.getZ() + 0.5D);
591 -@@ -453,7 +820,15 @@
598 +@@ -453,7 +822,15 @@
592 599 if (!this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.getWorldBorder().a(blockposition)) {
593 600 this.player.playerInteractManager.a(blockposition, packetplayinblockdig.b());
594 601 } else {
595 602 + // CraftBukkit start - fire PlayerInteractEvent
596 603 + CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, packetplayinblockdig.b(), this.player.inventory.getItemInHand(), EnumHand.MAIN_HAND);
597 604 this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition));
598 605 + // Update any tile entity data for this block
599 606 + TileEntity tileentity = worldserver.getTileEntity(blockposition);
600 607 + if (tileentity != null) {
601 608 + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket());
602 609 + }
603 610 + // CraftBukkit end
604 611 }
605 612 } else {
606 613 if (packetplayinblockdig.c() == PacketPlayInBlockDig.EnumPlayerDigType.STOP_DESTROY_BLOCK) {
607 -@@ -473,10 +848,12 @@
614 +@@ -473,10 +850,12 @@
608 615 default:
609 616 throw new IllegalArgumentException("Invalid player action");
610 617 }
611 618 + // CraftBukkit end
612 619 }
613 620
614 621 public void a(PacketPlayInUseItem packetplayinuseitem) {
615 622 PlayerConnectionUtils.ensureMainThread(packetplayinuseitem, this, this.player.x());
616 623 + if (this.player.cj()) return; // CraftBukkit
617 624 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
618 625 EnumHand enumhand = packetplayinuseitem.c();
619 626 ItemStack itemstack = this.player.b(enumhand);
620 -@@ -490,6 +867,13 @@
627 +@@ -490,6 +869,13 @@
621 628 chatmessage.getChatModifier().setColor(EnumChatFormat.RED);
622 629 this.player.playerConnection.sendPacket(new PacketPlayOutChat(chatmessage));
623 630 } else if (this.teleportPos == null && this.player.e((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D) < 64.0D && !this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.getWorldBorder().a(blockposition)) {
624 631 + // CraftBukkit start - Check if we can actually do something over this large a distance
625 632 + Location eyeLoc = this.getPlayer().getEyeLocation();
626 633 + double reachDistance = NumberConversions.square(eyeLoc.getX() - blockposition.getX()) + NumberConversions.square(eyeLoc.getY() - blockposition.getY()) + NumberConversions.square(eyeLoc.getZ() - blockposition.getZ());
627 634 + if (reachDistance > (this.getPlayer().getGameMode() == org.bukkit.GameMode.CREATIVE ? CREATIVE_PLACE_DISTANCE_SQUARED : SURVIVAL_PLACE_DISTANCE_SQUARED)) {
628 635 + return;
629 636 + }
630 637 + // CraftBukkit end
631 638 this.player.playerInteractManager.a(this.player, worldserver, itemstack, enumhand, blockposition, enumdirection, packetplayinuseitem.d(), packetplayinuseitem.e(), packetplayinuseitem.f());
632 639 }
633 640
634 -@@ -504,19 +888,55 @@
641 +@@ -504,19 +890,55 @@
635 642
636 643 public void a(PacketPlayInBlockPlace packetplayinblockplace) {
637 644 PlayerConnectionUtils.ensureMainThread(packetplayinblockplace, this, this.player.x());
638 645 + if (this.player.cj()) return; // CraftBukkit
639 646 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
640 647 EnumHand enumhand = packetplayinblockplace.a();
641 648 ItemStack itemstack = this.player.b(enumhand);
642 649
643 650 this.player.resetIdleTimer();
644 651 if (itemstack != null) {
685 692 + itemstack = this.player.b(enumhand);
686 693 + if (itemstack != null && itemstack.count == 0) {
687 694 + this.player.a(enumhand, (ItemStack) null);
688 695 + itemstack = null;
689 696 + }
690 697 + }
691 698 + // CraftBukkit end
692 699 }
693 700 }
694 701
695 -@@ -527,8 +947,8 @@
702 +@@ -527,8 +949,8 @@
696 703 WorldServer[] aworldserver = this.minecraftServer.worldServer;
697 704 int i = aworldserver.length;
698 705
699 706 - for (int j = 0; j < i; ++j) {
700 707 - WorldServer worldserver = aworldserver[j];
701 708 + // CraftBukkit - use the worlds array list
702 709 + for (WorldServer worldserver : minecraftServer.worlds) {
703 710
704 711 if (worldserver != null) {
705 712 entity = packetplayinspectate.a(worldserver);
706 -@@ -541,6 +961,8 @@
713 +@@ -541,6 +963,8 @@
707 714 if (entity != null) {
708 715 this.player.setSpectatorTarget(this.player);
709 716 this.player.stopRiding();
710 717 +
711 718 + /* CraftBukkit start - replace with bukkit handling for multi-world
712 719 if (entity.world == this.player.world) {
713 720 this.player.enderTeleportTo(entity.locX, entity.locY, entity.locZ);
714 721 } else {
715 -@@ -566,12 +988,19 @@
722 +@@ -566,12 +990,19 @@
716 723 this.minecraftServer.getPlayerList().b(this.player, worldserver2);
717 724 this.minecraftServer.getPlayerList().updateClient(this.player);
718 725 }
719 726 + */
720 727 + this.player.getBukkitEntity().teleport(entity.getBukkitEntity(), PlayerTeleportEvent.TeleportCause.SPECTATE);
721 728 + // CraftBukkit end
722 729 }
723 730 }
724 731
725 732 }
726 733
727 734 - public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) {}
728 735 + // CraftBukkit start
729 736 + public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) {
730 737 + this.server.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(getPlayer(), PlayerResourcePackStatusEvent.Status.values()[packetplayinresourcepackstatus.status.ordinal()]));
731 738 + }
732 739 + // CraftBukkit end
733 740
734 741 public void a(PacketPlayInBoatMove packetplayinboatmove) {
735 742 PlayerConnectionUtils.ensureMainThread(packetplayinboatmove, this, this.player.x());
736 -@@ -584,14 +1013,29 @@
743 +@@ -584,14 +1015,29 @@
737 744 }
738 745
739 746 public void a(IChatBaseComponent ichatbasecomponent) {
740 747 - PlayerConnection.LOGGER.info("{} lost connection: {}", new Object[] { this.player.getName(), ichatbasecomponent});
741 748 + // CraftBukkit start - Rarely it would send a disconnect line twice
742 749 + if (this.processedDisconnect) {
743 750 + return;
744 751 + } else {
745 752 + this.processedDisconnect = true;
746 753 + }
758 765 this.player.t();
759 766 - this.minecraftServer.getPlayerList().disconnect(this.player);
760 767 + String quitMessage = this.minecraftServer.getPlayerList().disconnect(this.player);
761 768 + if ((quitMessage != null) && (quitMessage.length() > 0)) {
762 769 + this.minecraftServer.getPlayerList().sendMessage(CraftChatMessage.fromString(quitMessage));
763 770 + }
764 771 + // CraftBukkit end
765 772 if (this.minecraftServer.R() && this.player.getName().equals(this.minecraftServer.Q())) {
766 773 PlayerConnection.LOGGER.info("Stopping singleplayer server as player logged out");
767 774 this.minecraftServer.safeShutdown();
768 -@@ -613,6 +1057,15 @@
775 +@@ -613,6 +1059,15 @@
769 776 }
770 777 }
771 778
772 779 + // CraftBukkit start
773 780 + if (packet == null) {
774 781 + return;
775 782 + } else if (packet instanceof PacketPlayOutSpawnPosition) {
776 783 + PacketPlayOutSpawnPosition packet6 = (PacketPlayOutSpawnPosition) packet;
777 784 + this.player.compassTarget = new Location(this.getPlayer().getWorld(), packet6.position.getX(), packet6.position.getY(), packet6.position.getZ());
778 785 + }
779 786 + // CraftBukkit end
780 787 +
781 788 try {
782 789 this.networkManager.sendPacket(packet);
783 790 } catch (Throwable throwable) {
784 -@@ -634,17 +1087,32 @@
791 +@@ -634,17 +1089,32 @@
785 792
786 793 public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) {
787 794 PlayerConnectionUtils.ensureMainThread(packetplayinhelditemslot, this, this.player.x());
788 795 + if (this.player.cj()) return; // CraftBukkit
789 796 if (packetplayinhelditemslot.a() >= 0 && packetplayinhelditemslot.a() < PlayerInventory.getHotbarSize()) {
790 797 + PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getPlayer(), this.player.inventory.itemInHandIndex, packetplayinhelditemslot.a());
791 798 + this.server.getPluginManager().callEvent(event);
792 799 + if (event.isCancelled()) {
793 800 + this.sendPacket(new PacketPlayOutHeldItemSlot(this.player.inventory.itemInHandIndex));
794 801 + this.player.resetIdleTimer();
809 816 + // CraftBukkit start - async chat
810 817 + boolean isSync = packetplayinchat.a().startsWith("/");
811 818 + if (packetplayinchat.a().startsWith("/")) {
812 819 + PlayerConnectionUtils.ensureMainThread(packetplayinchat, this, this.player.x());
813 820 + }
814 821 + // CraftBukkit end
815 822 + if (this.player.dead || this.player.getChatFlags() == EntityHuman.EnumChatVisibility.HIDDEN) { // CraftBukkit - dead men tell no tales
816 823 ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]);
817 824
818 825 chatmessage.getChatModifier().setColor(EnumChatFormat.RED);
819 -@@ -657,39 +1125,249 @@
826 +@@ -657,39 +1127,249 @@
820 827
821 828 for (int i = 0; i < s.length(); ++i) {
822 829 if (!SharedConstants.isAllowedChatCharacter(s.charAt(i))) {
823 830 - this.disconnect("Illegal characters in chat");
824 831 + // CraftBukkit start - threadsafety
825 832 + if (!isSync) {
826 833 + Waitable waitable = new Waitable() {
827 834 + @Override
828 835 + protected Object evaluate() {
829 836 + PlayerConnection.this.disconnect("Illegal characters in chat");
1067 1074 +
1068 1075 + if (e2.isCancelled()) {
1069 1076 + return;
1070 1077 + }
1071 1078 + break;
1072 1079 + }
1073 1080 + // CraftBukkit end
1074 1081 this.player.resetIdleTimer();
1075 1082 IJumpable ijumpable;
1076 1083
1077 -@@ -759,6 +1437,7 @@
1084 +@@ -759,6 +1439,7 @@
1078 1085
1079 1086 public void a(PacketPlayInUseEntity packetplayinuseentity) {
1080 1087 PlayerConnectionUtils.ensureMainThread(packetplayinuseentity, this, this.player.x());
1081 1088 + if (this.player.cj()) return; // CraftBukkit
1082 1089 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
1083 1090 Entity entity = packetplayinuseentity.a((World) worldserver);
1084 1091
1085 -@@ -775,22 +1454,72 @@
1092 +@@ -775,22 +1456,72 @@
1086 1093 EnumHand enumhand;
1087 1094 ItemStack itemstack;
1088 1095
1089 1096 + ItemStack itemInHand = this.player.b(packetplayinuseentity.b() == null ? EnumHand.MAIN_HAND : packetplayinuseentity.b()); // CraftBukkit
1090 1097 +
1091 1098 + if (packetplayinuseentity.a() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT
1092 1099 + || packetplayinuseentity.a() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT_AT) {
1093 1100 + // CraftBukkit start
1094 1101 + boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEAD && entity instanceof EntityInsentient;
1095 1102 + Item origItem = this.player.inventory.getItemInHand() == null ? null : this.player.inventory.getItemInHand().getItem();
1149 1156 this.player.attack(entity);
1150 1157 +
1151 1158 + // CraftBukkit start
1152 1159 + if (itemInHand != null && itemInHand.count <= -1) {
1153 1160 + this.player.updateInventory(this.player.activeContainer);
1154 1161 + }
1155 1162 + // CraftBukkit end
1156 1163 }
1157 1164 }
1158 1165 }
1159 -@@ -806,7 +1535,8 @@
1166 +@@ -806,7 +1537,8 @@
1160 1167 case 1:
1161 1168 if (this.player.viewingCredits) {
1162 1169 this.player.viewingCredits = false;
1163 1170 - this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, 0, true);
1164 1171 + // this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, 0, true);
1165 1172 + this.minecraftServer.getPlayerList().changeDimension(this.player, 0, PlayerTeleportEvent.TeleportCause.END_PORTAL); // CraftBukkit - reroute logic through custom portal management
1166 1173 } else {
1167 1174 if (this.player.getHealth() > 0.0F) {
1168 1175 return;
1169 -@@ -832,14 +1562,20 @@
1176 +@@ -832,14 +1564,20 @@
1170 1177
1171 1178 public void a(PacketPlayInCloseWindow packetplayinclosewindow) {
1172 1179 PlayerConnectionUtils.ensureMainThread(packetplayinclosewindow, this, this.player.x());
1173 1180 +
1174 1181 + if (this.player.cj()) return; // CraftBukkit
1175 1182 + CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit
1176 1183 +
1177 1184 this.player.s();
1178 1185 }
1179 1186
1180 1187 public void a(PacketPlayInWindowClick packetplayinwindowclick) {
1181 1188 PlayerConnectionUtils.ensureMainThread(packetplayinwindowclick, this, this.player.x());
1182 1189 + if (this.player.cj()) return; // CraftBukkit
1183 1190 this.player.resetIdleTimer();
1184 1191 if (this.player.activeContainer.windowId == packetplayinwindowclick.a() && this.player.activeContainer.c(this.player)) {
1185 1192 - if (this.player.isSpectator()) {
1186 1193 + boolean cancelled = this.player.isSpectator(); // CraftBukkit - see below if
1187 1194 + if (false/*this.player.isSpectator()*/) { // CraftBukkit
1188 1195 ArrayList arraylist = Lists.newArrayList();
1189 1196
1190 1197 for (int i = 0; i < this.player.activeContainer.c.size(); ++i) {
1191 -@@ -848,8 +1584,279 @@
1198 +@@ -848,8 +1586,279 @@
1192 1199
1193 1200 this.player.a(this.player.activeContainer, (List) arraylist);
1194 1201 } else {
1195 1202 - ItemStack itemstack = this.player.activeContainer.a(packetplayinwindowclick.b(), packetplayinwindowclick.c(), packetplayinwindowclick.f(), this.player);
1196 1203 + // CraftBukkit start - Call InventoryClickEvent
1197 1204 + if (packetplayinwindowclick.b() < -1 && packetplayinwindowclick.b() != -999) {
1198 1205 + return;
1199 1206 + }
1200 1207 +
1201 1208 + InventoryView inventory = this.player.activeContainer.getBukkitView();
1202 1209 + SlotType type = CraftInventoryView.getSlotType(inventory, packetplayinwindowclick.b());
1203 -
1210 ++
1204 1211 + InventoryClickEvent event;
1205 1212 + ClickType click = ClickType.UNKNOWN;
1206 1213 + InventoryAction action = InventoryAction.UNKNOWN;
1207 1214 +
1208 1215 + ItemStack itemstack = null;
1209 1216 +
1210 1217 + switch (packetplayinwindowclick.f()) {
1211 1218 + case PICKUP:
1212 1219 + if (packetplayinwindowclick.c() == 0) {
1213 1220 + click = ClickType.LEFT;
1369 1376 + action = InventoryAction.NOTHING;
1370 1377 + // Quick check for if we have any of the item
1371 1378 + if (inventory.getTopInventory().contains(org.bukkit.Material.getMaterial(Item.getId(cursor.getItem()))) || inventory.getBottomInventory().contains(org.bukkit.Material.getMaterial(Item.getId(cursor.getItem())))) {
1372 1379 + action = InventoryAction.COLLECT_TO_CURSOR;
1373 1380 + }
1374 1381 + }
1375 1382 + break;
1376 1383 + default:
1377 1384 + break;
1378 1385 + }
1379 -+
1386 +
1380 1387 + if (packetplayinwindowclick.f() != InventoryClickType.QUICK_CRAFT) {
1381 1388 + if (click == ClickType.NUMBER_KEY) {
1382 1389 + event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.b(), click, action, packetplayinwindowclick.c());
1383 1390 + } else {
1384 1391 + event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.b(), click, action);
1385 1392 + }
1386 1393 +
1387 1394 + org.bukkit.inventory.Inventory top = inventory.getTopInventory();
1388 1395 + if (packetplayinwindowclick.b() == 0 && top instanceof CraftingInventory) {
1389 1396 + org.bukkit.inventory.Recipe recipe = ((CraftingInventory) top).getRecipe();
1462 1469 + if (event instanceof CraftItemEvent) {
1463 1470 + // Need to update the inventory on crafting to
1464 1471 + // correctly support custom recipes
1465 1472 + player.updateInventory(player.activeContainer);
1466 1473 + }
1467 1474 + }
1468 1475 + // CraftBukkit end
1469 1476 if (ItemStack.matches(packetplayinwindowclick.e(), itemstack)) {
1470 1477 this.player.playerConnection.sendPacket(new PacketPlayOutTransaction(packetplayinwindowclick.a(), packetplayinwindowclick.d(), true));
1471 1478 this.player.f = true;
1472 -@@ -878,6 +1885,7 @@
1479 +@@ -878,6 +1887,7 @@
1473 1480
1474 1481 public void a(PacketPlayInEnchantItem packetplayinenchantitem) {
1475 1482 PlayerConnectionUtils.ensureMainThread(packetplayinenchantitem, this, this.player.x());
1476 1483 + if (this.player.cj()) return; // CraftBukkit
1477 1484 this.player.resetIdleTimer();
1478 1485 if (this.player.activeContainer.windowId == packetplayinenchantitem.a() && this.player.activeContainer.c(this.player) && !this.player.isSpectator()) {
1479 1486 this.player.activeContainer.a(this.player, packetplayinenchantitem.b());
1480 -@@ -911,8 +1919,48 @@
1487 +@@ -911,8 +1921,48 @@
1481 1488 }
1482 1489
1483 1490 boolean flag1 = packetplayinsetcreativeslot.a() >= 1 && packetplayinsetcreativeslot.a() <= 45;
1484 1491 - boolean flag2 = itemstack == null || itemstack.getItem() != null;
1485 1492 + // CraftBukkit - Add invalidItems check
1486 1493 + boolean flag2 = itemstack == null || itemstack.getItem() != null && !invalidItems.contains(Item.getId(itemstack.getItem()));
1487 1494 boolean flag3 = itemstack == null || itemstack.getData() >= 0 && itemstack.count <= 64 && itemstack.count > 0;
1488 1495 + // CraftBukkit start - Call click event
1489 1496 + if (flag || (flag1 && !ItemStack.matches(this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.a()).getItem(), packetplayinsetcreativeslot.getItemStack()))) { // Insist on valid slot
1490 1497 +
1520 1527 + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.defaultContainer.windowId, packetplayinsetcreativeslot.a(), this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.a()).getItem()));
1521 1528 + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, null));
1522 1529 + }
1523 1530 + return;
1524 1531 + }
1525 1532 + }
1526 1533 + // CraftBukkit end
1527 1534
1528 1535 if (flag1 && flag2 && flag3) {
1529 1536 if (itemstack == null) {
1530 -@@ -936,6 +1984,7 @@
1537 +@@ -936,6 +1986,7 @@
1531 1538
1532 1539 public void a(PacketPlayInTransaction packetplayintransaction) {
1533 1540 PlayerConnectionUtils.ensureMainThread(packetplayintransaction, this, this.player.x());
1534 1541 + if (this.player.cj()) return; // CraftBukkit
1535 1542 Short oshort = (Short) this.k.get(this.player.activeContainer.windowId);
1536 1543
1537 1544 if (oshort != null && packetplayintransaction.b() == oshort.shortValue() && this.player.activeContainer.windowId == packetplayintransaction.a() && !this.player.activeContainer.c(this.player) && !this.player.isSpectator()) {
1538 -@@ -946,6 +1995,7 @@
1545 +@@ -946,6 +1997,7 @@
1539 1546
1540 1547 public void a(PacketPlayInUpdateSign packetplayinupdatesign) {
1541 1548 PlayerConnectionUtils.ensureMainThread(packetplayinupdatesign, this, this.player.x());
1542 1549 + if (this.player.cj()) return; // CraftBukkit
1543 1550 this.player.resetIdleTimer();
1544 1551 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
1545 1552 BlockPosition blockposition = packetplayinupdatesign.a();
1546 -@@ -962,14 +2012,30 @@
1553 +@@ -962,14 +2014,30 @@
1547 1554
1548 1555 if (!tileentitysign.d() || tileentitysign.e() != this.player) {
1549 1556 this.minecraftServer.warning("Player " + this.player.getName() + " just tried to change non-editable sign");
1550 1557 + this.sendPacket(tileentity.getUpdatePacket()); // CraftBukkit
1551 1558 return;
1552 1559 }
1553 1560
1554 1561 String[] astring = packetplayinupdatesign.b();
1555 1562
1556 1563 + // CraftBukkit start
1568 1575 + this.server.getPluginManager().callEvent(event);
1569 1576 +
1570 1577 + if (!event.isCancelled()) {
1571 1578 + System.arraycopy(org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.getLines()), 0, tileentitysign.lines, 0, 4);
1572 1579 + tileentitysign.isEditable = false;
1573 1580 + }
1574 1581 + // CraftBukkit end
1575 1582
1576 1583 tileentitysign.update();
1577 1584 worldserver.notify(blockposition, iblockdata, iblockdata, 3);
1578 -@@ -992,11 +2058,27 @@
1585 +@@ -992,11 +2060,27 @@
1579 1586
1580 1587 public void a(PacketPlayInAbilities packetplayinabilities) {
1581 1588 PlayerConnectionUtils.ensureMainThread(packetplayinabilities, this, this.player.x());
1582 1589 - this.player.abilities.isFlying = packetplayinabilities.isFlying() && this.player.abilities.canFly;
1583 1590 + // CraftBukkit start
1584 1591 + if (this.player.abilities.canFly && this.player.abilities.isFlying != packetplayinabilities.isFlying()) {
1585 1592 + PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.server.getPlayer(this.player), packetplayinabilities.isFlying());
1586 1593 + this.server.getPluginManager().callEvent(event);
1587 1594 + if (!event.isCancelled()) {
1588 1595 + this.player.abilities.isFlying = packetplayinabilities.isFlying(); // Actually set the player's flying status
1597 1604 PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.x());
1598 1605 + // CraftBukkit start
1599 1606 + if (chatSpamField.addAndGet(this, 10) > 500 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) {
1600 1607 + this.disconnect("disconnect.spam");
1601 1608 + return;
1602 1609 + }
1603 1610 + // CraftBukkit end
1604 1611 ArrayList arraylist = Lists.newArrayList();
1605 1612 Iterator iterator = this.minecraftServer.tabCompleteCommand(this.player, packetplayintabcomplete.a(), packetplayintabcomplete.b(), packetplayintabcomplete.c()).iterator();
1606 1613
1607 -@@ -1040,10 +2122,13 @@
1614 +@@ -1040,10 +2124,13 @@
1608 1615 }
1609 1616
1610 1617 if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack.getItem() == itemstack1.getItem()) {
1611 1618 + itemstack1 = new ItemStack(Items.WRITABLE_BOOK); // CraftBukkit
1612 1619 itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8));
1613 1620 + CraftEventFactory.handleEditBookEvent(player, itemstack1); // CraftBukkit
1614 1621 }
1615 1622 } catch (Exception exception) {
1616 1623 PlayerConnection.LOGGER.error("Couldn\'t handle book info", exception);
1617 1624 + this.disconnect("Invalid book data!"); // CraftBukkit
1618 1625 }
1619 1626 } else {
1620 1627 String s1;
1621 -@@ -1067,6 +2152,7 @@
1628 +@@ -1067,6 +2154,7 @@
1622 1629 }
1623 1630
1624 1631 if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack1.getItem() == Items.WRITABLE_BOOK) {
1625 1632 + itemstack1 = new ItemStack(Items.WRITABLE_BOOK); // CraftBukkit
1626 1633 itemstack1.a("author", (NBTBase) (new NBTTagString(this.player.getName())));
1627 1634 itemstack1.a("title", (NBTBase) (new NBTTagString(itemstack.getTag().getString("title"))));
1628 1635 NBTTagList nbttaglist = itemstack.getTag().getList("pages", 8);
1629 -@@ -1081,9 +2167,11 @@
1636 +@@ -1081,9 +2169,11 @@
1630 1637
1631 1638 itemstack1.a("pages", (NBTBase) nbttaglist);
1632 1639 itemstack1.setItem(Items.WRITTEN_BOOK);
1633 1640 + CraftEventFactory.handleEditBookEvent(player, itemstack1); // CraftBukkit
1634 1641 }
1635 1642 } catch (Exception exception1) {
1636 1643 PlayerConnection.LOGGER.error("Couldn\'t sign book", exception1);
1637 1644 + this.disconnect("Invalid book data!"); // CraftBukkit
1638 1645 }
1639 1646 } else if ("MC|TrSel".equals(s)) {
1640 1647 try {
1641 -@@ -1095,6 +2183,7 @@
1648 +@@ -1095,6 +2185,7 @@
1642 1649 }
1643 1650 } catch (Exception exception2) {
1644 1651 PlayerConnection.LOGGER.error("Couldn\'t select trade", exception2);
1645 1652 + this.disconnect("Invalid trade data!"); // CraftBukkit
1646 1653 }
1647 1654 } else {
1648 1655 TileEntity tileentity;
1649 -@@ -1144,6 +2233,7 @@
1656 +@@ -1144,6 +2235,7 @@
1650 1657 }
1651 1658 } catch (Exception exception3) {
1652 1659 PlayerConnection.LOGGER.error("Couldn\'t set command block", exception3);
1653 1660 + this.disconnect("Invalid command data!"); // CraftBukkit
1654 1661 }
1655 1662 } else if ("MC|AutoCmd".equals(s)) {
1656 1663 if (!this.minecraftServer.getEnableCommandBlock()) {
1657 -@@ -1211,6 +2301,7 @@
1664 +@@ -1211,6 +2303,7 @@
1658 1665 }
1659 1666 } catch (Exception exception4) {
1660 1667 PlayerConnection.LOGGER.error("Couldn\'t set command block", exception4);
1661 1668 + this.disconnect("Invalid command data!"); // CraftBukkit
1662 1669 }
1663 1670 } else {
1664 1671 int k;
1665 -@@ -1234,6 +2325,7 @@
1672 +@@ -1234,6 +2327,7 @@
1666 1673 }
1667 1674 } catch (Exception exception5) {
1668 1675 PlayerConnection.LOGGER.error("Couldn\'t set beacon", exception5);
1669 1676 + this.disconnect("Invalid beacon data!"); // CraftBukkit
1670 1677 }
1671 1678 }
1672 1679 } else if ("MC|ItemName".equals(s)) {
1673 -@@ -1320,6 +2412,7 @@
1680 +@@ -1320,6 +2414,7 @@
1674 1681 }
1675 1682 } catch (Exception exception6) {
1676 1683 PlayerConnection.LOGGER.error("Couldn\'t set structure block", exception6);
1677 1684 + this.disconnect("Invalid structure data!"); // CraftBukkit
1678 1685 }
1679 1686 } else if ("MC|PickItem".equals(s)) {
1680 1687 packetdataserializer = packetplayincustompayload.b();
1681 -@@ -1332,14 +2425,37 @@
1688 +@@ -1332,14 +2427,37 @@
1682 1689 this.player.playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(this.player.inventory.itemInHandIndex));
1683 1690 } catch (Exception exception7) {
1684 1691 PlayerConnection.LOGGER.error("Couldn\'t pick item", exception7);
1685 1692 + this.disconnect("Invalid item data!"); // CraftBukkit
1686 1693 + }
1687 1694 + }
1688 1695 + // CraftBukkit start
1689 1696 + else if (packetplayincustompayload.a().equals("REGISTER")) {
1690 1697 + String channels = packetplayincustompayload.b().toString(com.google.common.base.Charsets.UTF_8);
1691 1698 + for (String channel : channels.split("\0")) {

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

Add shortcut