Commits

md_5 authored 02f4218da51
SPIGOT-2414: Repeated disconnects
No tags

nms-patches/PlayerConnection.patch

Modified
118 118
119 119 if (this.j > 0) {
120 120 --this.j;
121 121 }
122 122
123 123 if (this.player.I() > 0L && this.minecraftServer.getIdleTimeout() > 0 && MinecraftServer.av() - this.player.I() > (long) (this.minecraftServer.getIdleTimeout() * 1000 * 60)) {
124 124 + this.player.resetIdleTimer(); // CraftBukkit - SPIGOT-854
125 125 this.disconnect("You have been idle for too long!");
126 126 }
127 127
128 -@@ -138,19 +216,37 @@
128 +@@ -138,19 +216,40 @@
129 129 }
130 130
131 131 public void disconnect(String s) {
132 132 + // CraftBukkit start - fire PlayerKickEvent
133 ++ if (this.processedDisconnect) {
134 ++ return;
135 ++ }
133 136 + String leaveMessage = EnumChatFormat.YELLOW + this.player.getName() + " left the game.";
134 137 +
135 138 + PlayerKickEvent event = new PlayerKickEvent(this.server.getPlayer(this.player), s, leaveMessage);
136 139 +
137 140 + if (this.server.getServer().isRunning()) {
138 141 + this.server.getPluginManager().callEvent(event);
139 142 + }
140 143 +
141 144 + if (event.isCancelled()) {
142 145 + // Do not kick the player
159 162 + // CraftBukkit - Don't wait
160 163 + this.minecraftServer.postToMainThread(new Runnable() {
161 164 public void run() {
162 165 PlayerConnection.this.networkManager.handleDisconnection();
163 166 }
164 167 - }));
165 168 + });
166 169 }
167 170
168 171 public void a(PacketPlayInSteerVehicle packetplayinsteervehicle) {
169 -@@ -189,7 +285,34 @@
172 +@@ -189,7 +288,34 @@
170 173 double d9 = entity.motX * entity.motX + entity.motY * entity.motY + entity.motZ * entity.motZ;
171 174 double d10 = d6 * d6 + d7 * d7 + d8 * d8;
172 175
173 176 - if (d10 - d9 > 100.0D && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(entity.getName()))) {
174 177 +
175 178 + // CraftBukkit start - handle custom speeds and skipped ticks
176 179 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
177 180 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
178 181 + this.lastTick = (int) (System.currentTimeMillis() / 50);
179 182 +
195 198 + } else {
196 199 + speed = player.abilities.walkSpeed * 10f;
197 200 + }
198 201 + speed *= 2f; // TODO: Get the speed of the vehicle instead of the player
199 202 +
200 203 + if (d10 - d9 > Math.max(100, Math.pow((double) (10.0F * (float) i * speed), 2)) && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(entity.getName()))) {
201 204 + // CraftBukkit end
202 205 PlayerConnection.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", new Object[] { entity.getName(), this.player.getName(), Double.valueOf(d6), Double.valueOf(d7), Double.valueOf(d8)});
203 206 this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity));
204 207 return;
205 -@@ -227,6 +350,62 @@
208 +@@ -227,6 +353,62 @@
206 209 return;
207 210 }
208 211
209 212 + // CraftBukkit start - fire PlayerMoveEvent
210 213 + Player player = this.getPlayer();
211 214 + Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location.
212 215 + Location to = player.getLocation().clone(); // Start off the To location as the Players current location.
213 216 +
214 217 + // If the packet contains movement information then we update the To location with the correct XYZ.
215 218 + to.setX(packetplayinvehiclemove.getX());
258 261 + this.justTeleported = false;
259 262 + return;
260 263 + }
261 264 + }
262 265 + }
263 266 + // CraftBukkit end
264 267 +
265 268 this.minecraftServer.getPlayerList().d(this.player);
266 269 this.player.checkMovement(this.player.locX - d0, this.player.locY - d1, this.player.locZ - d2);
267 270 this.D = d11 >= -0.03125D && !this.minecraftServer.getAllowFlight() && !worldserver.d(entity.getBoundingBox().g(0.0625D).a(0.0D, -0.55D, 0.0D));
268 -@@ -261,7 +440,7 @@
271 +@@ -261,7 +443,7 @@
269 272 } else {
270 273 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
271 274
272 275 - if (!this.player.viewingCredits) {
273 276 + if (!this.player.viewingCredits && !this.player.dead) { // CraftBukkit - Added ' && !this.player.dead'
274 277 if (this.e == 0) {
275 278 this.d();
276 279 }
277 -@@ -271,13 +450,21 @@
280 +@@ -271,13 +453,21 @@
278 281 this.A = this.e;
279 282 this.a(this.teleportPos.x, this.teleportPos.y, this.teleportPos.z, this.player.yaw, this.player.pitch);
280 283 }
281 284 -
282 285 + this.allowedPlayerTicks = 20; // CraftBukkit
283 286 } else {
284 287 this.A = this.e;
285 288 if (this.player.isPassenger()) {
286 289 this.player.setLocation(this.player.locX, this.player.locY, this.player.locZ, packetplayinflying.a(this.player.yaw), packetplayinflying.b(this.player.pitch));
287 290 this.minecraftServer.getPlayerList().d(this.player);
290 293 + // CraftBukkit - Make sure the move is valid but then reset it for plugins to modify
291 294 + double prevX = player.locX;
292 295 + double prevY = player.locY;
293 296 + double prevZ = player.locZ;
294 297 + float prevYaw = player.yaw;
295 298 + float prevPitch = player.pitch;
296 299 + // CraftBukkit end
297 300 double d0 = this.player.locX;
298 301 double d1 = this.player.locY;
299 302 double d2 = this.player.locZ;
300 -@@ -296,15 +483,33 @@
303 +@@ -296,15 +486,33 @@
301 304 ++this.F;
302 305 int i = this.F - this.G;
303 306
304 307 - if (i > 5) {
305 308 - PlayerConnection.LOGGER.debug("{} is sending move packets too frequently ({} packets since last tick)", new Object[] { this.player.getName(), Integer.valueOf(i)});
306 309 + // CraftBukkit start - handle custom speeds and skipped ticks
307 310 + this.allowedPlayerTicks += (System.currentTimeMillis() / 50) - this.lastTick;
308 311 + this.allowedPlayerTicks = Math.max(this.allowedPlayerTicks, 1);
309 312 + this.lastTick = (int) (System.currentTimeMillis() / 50);
310 313 +
327 330 +
328 331 if (!this.player.K() && (!this.player.x().getGameRules().getBoolean("disableElytraMovementCheck") || !this.player.cG())) {
329 332 float f2 = this.player.cG() ? 300.0F : 100.0F;
330 333
331 334 - if (d11 - d10 > (double) (f2 * (float) i) && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(this.player.getName()))) {
332 335 + 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()))) {
333 336 + // CraftBukkit end
334 337 PlayerConnection.LOGGER.warn("{} moved too quickly! {},{},{}", new Object[] { this.player.getName(), Double.valueOf(d7), Double.valueOf(d8), Double.valueOf(d9)});
335 338 this.a(this.player.locX, this.player.locY, this.player.locZ, this.player.yaw, this.player.pitch);
336 339 return;
337 -@@ -350,6 +555,69 @@
340 +@@ -350,6 +558,69 @@
338 341 }
339 342 }
340 343
341 344 + // CraftBukkit start - fire PlayerMoveEvent
342 345 + // Rest to old location first
343 346 + this.player.setLocation(prevX, prevY, prevZ, prevYaw, prevPitch);
344 347 +
345 348 + Player player = this.getPlayer();
346 349 + Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location.
347 350 + Location to = player.getLocation().clone(); // Start off the To location as the Players current location.
397 400 + return;
398 401 + }
399 402 + }
400 403 + }
401 404 + this.player.setLocation(d4, d5, d6, f, f1); // Copied from above
402 405 + // CraftBukkit end
403 406 +
404 407 this.B = d12 >= -0.03125D;
405 408 this.B &= !this.minecraftServer.getAllowFlight() && !this.player.abilities.canFly;
406 409 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));
407 -@@ -366,15 +634,79 @@
410 +@@ -366,15 +637,79 @@
408 411 }
409 412
410 413 public void a(double d0, double d1, double d2, float f, float f1) {
411 414 - this.a(d0, d1, d2, f, f1, Collections.emptySet());
412 415 + this.a(d0, d1, d2, f, f1, Collections.<PacketPlayOutPosition.EnumPlayerTeleportFlags>emptySet());
413 416 }
414 417
415 418 public void a(double d0, double d1, double d2, float f, float f1, Set<PacketPlayOutPosition.EnumPlayerTeleportFlags> set) {
416 419 - double d3 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.X) ? this.player.locX : 0.0D;
417 420 - double d4 = set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y) ? this.player.locY : 0.0D;
451 454 + to = event.isCancelled() ? event.getFrom() : event.getTo();
452 455 + d0 = to.getX();
453 456 + d1 = to.getY();
454 457 + d2 = to.getZ();
455 458 + f = to.getYaw();
456 459 + f1 = to.getPitch();
457 460 + }
458 461 +
459 462 + this.internalTeleport(d0, d1, d2, f, f1, set);
460 463 + }
461 -
462 -- this.teleportPos = new Vec3D(d0 + d3, d1 + d4, d2 + d5);
464 ++
463 465 + public void teleport(Location dest) {
464 466 + internalTeleport(dest.getX(), dest.getY(), dest.getZ(), dest.getYaw(), dest.getPitch(), Collections.emptySet());
465 467 + }
466 468 +
467 469 + private void internalTeleport(double d0, double d1, double d2, float f, float f1, Set set) {
468 470 + if (Float.isNaN(f)) {
469 471 + f = 0;
470 472 + }
471 473 +
472 474 + if (Float.isNaN(f1)) {
473 475 + f1 = 0;
474 476 + }
475 477 + this.justTeleported = true;
476 478 + this.teleportPos = new Vec3D(d0, d1, d2);
477 479 + if (set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.X)) {
478 480 + this.teleportPos = this.teleportPos.add(this.player.locX, 0.0D, 0.0D);
479 481 + }
480 482 +
481 483 + if (set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Y)) {
482 484 + this.teleportPos = this.teleportPos.add(0.0D, this.player.locY, 0.0D);
483 485 + }
484 -+
486 +
487 +- this.teleportPos = new Vec3D(d0 + d3, d1 + d4, d2 + d5);
485 488 + if (set.contains(PacketPlayOutPosition.EnumPlayerTeleportFlags.Z)) {
486 489 + this.teleportPos = this.teleportPos.add(0.0D, 0.0D, this.player.locZ);
487 490 + }
488 491 + // CraftBukkit end
489 492 float f2 = f;
490 493 float f3 = f1;
491 494
492 -@@ -386,6 +718,14 @@
495 +@@ -386,6 +721,14 @@
493 496 f3 = f1 + this.player.pitch;
494 497 }
495 498
496 499 + // CraftBukkit start - update last location
497 500 + this.lastPosX = this.teleportPos.x;
498 501 + this.lastPosY = this.teleportPos.y;
499 502 + this.lastPosZ = this.teleportPos.z;
500 503 + this.lastYaw = f2;
501 504 + this.lastPitch = f3;
502 505 + // CraftBukkit end
503 506 +
504 507 if (++this.teleportAwait == Integer.MAX_VALUE) {
505 508 this.teleportAwait = 0;
506 509 }
507 -@@ -397,37 +737,61 @@
510 +@@ -397,37 +740,61 @@
508 511
509 512 public void a(PacketPlayInBlockDig packetplayinblockdig) {
510 513 PlayerConnectionUtils.ensureMainThread(packetplayinblockdig, this, this.player.x());
511 514 + if (this.player.dead) return; // CraftBukkit
512 515 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
513 516 BlockPosition blockposition = packetplayinblockdig.a();
514 517
515 518 this.player.resetIdleTimer();
516 519 ItemStack itemstack;
517 520
565 568 this.player.a(true);
566 569 }
567 570
568 571 return;
569 572
570 573 - case 4:
571 574 + case 4: // RELEASE_USE_ITEM
572 575 this.player.clearActiveItem();
573 576 itemstack = this.player.getItemInMainHand();
574 577 if (itemstack != null && itemstack.count == 0) {
575 -@@ -436,9 +800,9 @@
578 +@@ -436,9 +803,9 @@
576 579
577 580 return;
578 581
579 582 - case 5:
580 583 - case 6:
581 584 - case 7:
582 585 + case 5: // START_DESTROY_BLOCK
583 586 + case 6: // ABORT_DESTROY_BLOCK
584 587 + case 7: // STOP_DESTROY_BLOCK
585 588 double d0 = this.player.locX - ((double) blockposition.getX() + 0.5D);
586 589 double d1 = this.player.locY - ((double) blockposition.getY() + 0.5D) + 1.5D;
587 590 double d2 = this.player.locZ - ((double) blockposition.getZ() + 0.5D);
588 -@@ -453,7 +817,15 @@
591 +@@ -453,7 +820,15 @@
589 592 if (!this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.getWorldBorder().a(blockposition)) {
590 593 this.player.playerInteractManager.a(blockposition, packetplayinblockdig.b());
591 594 } else {
592 595 + // CraftBukkit start - fire PlayerInteractEvent
593 596 + CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, packetplayinblockdig.b(), this.player.inventory.getItemInHand(), EnumHand.MAIN_HAND);
594 597 this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition));
595 598 + // Update any tile entity data for this block
596 599 + TileEntity tileentity = worldserver.getTileEntity(blockposition);
597 600 + if (tileentity != null) {
598 601 + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket());
599 602 + }
600 603 + // CraftBukkit end
601 604 }
602 605 } else {
603 606 if (packetplayinblockdig.c() == PacketPlayInBlockDig.EnumPlayerDigType.STOP_DESTROY_BLOCK) {
604 -@@ -473,10 +845,12 @@
607 +@@ -473,10 +848,12 @@
605 608 default:
606 609 throw new IllegalArgumentException("Invalid player action");
607 610 }
608 611 + // CraftBukkit end
609 612 }
610 613
611 614 public void a(PacketPlayInUseItem packetplayinuseitem) {
612 615 PlayerConnectionUtils.ensureMainThread(packetplayinuseitem, this, this.player.x());
613 616 + if (this.player.dead) return; // CraftBukkit
614 617 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
615 618 EnumHand enumhand = packetplayinuseitem.c();
616 619 ItemStack itemstack = this.player.b(enumhand);
617 -@@ -490,6 +864,13 @@
620 +@@ -490,6 +867,13 @@
618 621 chatmessage.getChatModifier().setColor(EnumChatFormat.RED);
619 622 this.player.playerConnection.sendPacket(new PacketPlayOutChat(chatmessage));
620 623 } 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)) {
621 624 + // CraftBukkit start - Check if we can actually do something over this large a distance
622 625 + Location eyeLoc = this.getPlayer().getEyeLocation();
623 626 + double reachDistance = NumberConversions.square(eyeLoc.getX() - blockposition.getX()) + NumberConversions.square(eyeLoc.getY() - blockposition.getY()) + NumberConversions.square(eyeLoc.getZ() - blockposition.getZ());
624 627 + if (reachDistance > (this.getPlayer().getGameMode() == org.bukkit.GameMode.CREATIVE ? CREATIVE_PLACE_DISTANCE_SQUARED : SURVIVAL_PLACE_DISTANCE_SQUARED)) {
625 628 + return;
626 629 + }
627 630 + // CraftBukkit end
628 631 this.player.playerInteractManager.a(this.player, worldserver, itemstack, enumhand, blockposition, enumdirection, packetplayinuseitem.d(), packetplayinuseitem.e(), packetplayinuseitem.f());
629 632 }
630 633
631 -@@ -504,19 +885,55 @@
634 +@@ -504,19 +888,55 @@
632 635
633 636 public void a(PacketPlayInBlockPlace packetplayinblockplace) {
634 637 PlayerConnectionUtils.ensureMainThread(packetplayinblockplace, this, this.player.x());
635 638 + if (this.player.dead) return; // CraftBukkit
636 639 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
637 640 EnumHand enumhand = packetplayinblockplace.a();
638 641 ItemStack itemstack = this.player.b(enumhand);
639 642
640 643 this.player.resetIdleTimer();
641 644 if (itemstack != null) {
682 685 + itemstack = this.player.b(enumhand);
683 686 + if (itemstack != null && itemstack.count == 0) {
684 687 + this.player.a(enumhand, (ItemStack) null);
685 688 + itemstack = null;
686 689 + }
687 690 + }
688 691 + // CraftBukkit end
689 692 }
690 693 }
691 694
692 -@@ -527,8 +944,8 @@
695 +@@ -527,8 +947,8 @@
693 696 WorldServer[] aworldserver = this.minecraftServer.worldServer;
694 697 int i = aworldserver.length;
695 698
696 699 - for (int j = 0; j < i; ++j) {
697 700 - WorldServer worldserver = aworldserver[j];
698 701 + // CraftBukkit - use the worlds array list
699 702 + for (WorldServer worldserver : minecraftServer.worlds) {
700 703
701 704 if (worldserver != null) {
702 705 entity = packetplayinspectate.a(worldserver);
703 -@@ -541,6 +958,8 @@
706 +@@ -541,6 +961,8 @@
704 707 if (entity != null) {
705 708 this.player.setSpectatorTarget(this.player);
706 709 this.player.stopRiding();
707 710 +
708 711 + /* CraftBukkit start - replace with bukkit handling for multi-world
709 712 if (entity.world == this.player.world) {
710 713 this.player.enderTeleportTo(entity.locX, entity.locY, entity.locZ);
711 714 } else {
712 -@@ -566,12 +985,19 @@
715 +@@ -566,12 +988,19 @@
713 716 this.minecraftServer.getPlayerList().b(this.player, worldserver2);
714 717 this.minecraftServer.getPlayerList().updateClient(this.player);
715 718 }
716 719 + */
717 720 + this.player.getBukkitEntity().teleport(entity.getBukkitEntity(), PlayerTeleportEvent.TeleportCause.SPECTATE);
718 721 + // CraftBukkit end
719 722 }
720 723 }
721 724
722 725 }
723 726
724 727 - public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) {}
725 728 + // CraftBukkit start
726 729 + public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) {
727 730 + this.server.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(getPlayer(), PlayerResourcePackStatusEvent.Status.values()[packetplayinresourcepackstatus.status.ordinal()]));
728 731 + }
729 732 + // CraftBukkit end
730 733
731 734 public void a(PacketPlayInBoatMove packetplayinboatmove) {
732 735 PlayerConnectionUtils.ensureMainThread(packetplayinboatmove, this, this.player.x());
733 -@@ -584,14 +1010,29 @@
736 +@@ -584,14 +1013,29 @@
734 737 }
735 738
736 739 public void a(IChatBaseComponent ichatbasecomponent) {
737 740 - PlayerConnection.LOGGER.info("{} lost connection: {}", new Object[] { this.player.getName(), ichatbasecomponent});
738 741 + // CraftBukkit start - Rarely it would send a disconnect line twice
739 742 + if (this.processedDisconnect) {
740 743 + return;
741 744 + } else {
742 745 + this.processedDisconnect = true;
743 746 + }
755 758 this.player.t();
756 759 - this.minecraftServer.getPlayerList().disconnect(this.player);
757 760 + String quitMessage = this.minecraftServer.getPlayerList().disconnect(this.player);
758 761 + if ((quitMessage != null) && (quitMessage.length() > 0)) {
759 762 + this.minecraftServer.getPlayerList().sendMessage(CraftChatMessage.fromString(quitMessage));
760 763 + }
761 764 + // CraftBukkit end
762 765 if (this.minecraftServer.R() && this.player.getName().equals(this.minecraftServer.Q())) {
763 766 PlayerConnection.LOGGER.info("Stopping singleplayer server as player logged out");
764 767 this.minecraftServer.safeShutdown();
765 -@@ -613,6 +1054,15 @@
768 +@@ -613,6 +1057,15 @@
766 769 }
767 770 }
768 771
769 772 + // CraftBukkit start
770 773 + if (packet == null) {
771 774 + return;
772 775 + } else if (packet instanceof PacketPlayOutSpawnPosition) {
773 776 + PacketPlayOutSpawnPosition packet6 = (PacketPlayOutSpawnPosition) packet;
774 777 + this.player.compassTarget = new Location(this.getPlayer().getWorld(), packet6.position.getX(), packet6.position.getY(), packet6.position.getZ());
775 778 + }
776 779 + // CraftBukkit end
777 780 +
778 781 try {
779 782 this.networkManager.sendPacket(packet);
780 783 } catch (Throwable throwable) {
781 -@@ -634,17 +1084,32 @@
784 +@@ -634,17 +1087,32 @@
782 785
783 786 public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) {
784 787 PlayerConnectionUtils.ensureMainThread(packetplayinhelditemslot, this, this.player.x());
785 788 + if (this.player.dead) return; // CraftBukkit
786 789 if (packetplayinhelditemslot.a() >= 0 && packetplayinhelditemslot.a() < PlayerInventory.getHotbarSize()) {
787 790 + PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getPlayer(), this.player.inventory.itemInHandIndex, packetplayinhelditemslot.a());
788 791 + this.server.getPluginManager().callEvent(event);
789 792 + if (event.isCancelled()) {
790 793 + this.sendPacket(new PacketPlayOutHeldItemSlot(this.player.inventory.itemInHandIndex));
791 794 + this.player.resetIdleTimer();
806 809 + // CraftBukkit start - async chat
807 810 + boolean isSync = packetplayinchat.a().startsWith("/");
808 811 + if (packetplayinchat.a().startsWith("/")) {
809 812 + PlayerConnectionUtils.ensureMainThread(packetplayinchat, this, this.player.x());
810 813 + }
811 814 + // CraftBukkit end
812 815 + if (this.player.dead || this.player.getChatFlags() == EntityHuman.EnumChatVisibility.HIDDEN) { // CraftBukkit - dead men tell no tales
813 816 ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]);
814 817
815 818 chatmessage.getChatModifier().setColor(EnumChatFormat.RED);
816 -@@ -657,39 +1122,249 @@
819 +@@ -657,39 +1125,249 @@
817 820
818 821 for (int i = 0; i < s.length(); ++i) {
819 822 if (!SharedConstants.isAllowedChatCharacter(s.charAt(i))) {
820 823 - this.disconnect("Illegal characters in chat");
821 824 + // CraftBukkit start - threadsafety
822 825 + if (!isSync) {
823 826 + Waitable waitable = new Waitable() {
824 827 + @Override
825 828 + protected Object evaluate() {
826 829 + PlayerConnection.this.disconnect("Illegal characters in chat");
1064 1067 +
1065 1068 + if (e2.isCancelled()) {
1066 1069 + return;
1067 1070 + }
1068 1071 + break;
1069 1072 + }
1070 1073 + // CraftBukkit end
1071 1074 this.player.resetIdleTimer();
1072 1075 IJumpable ijumpable;
1073 1076
1074 -@@ -759,6 +1434,7 @@
1077 +@@ -759,6 +1437,7 @@
1075 1078
1076 1079 public void a(PacketPlayInUseEntity packetplayinuseentity) {
1077 1080 PlayerConnectionUtils.ensureMainThread(packetplayinuseentity, this, this.player.x());
1078 1081 + if (this.player.dead) return; // CraftBukkit
1079 1082 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
1080 1083 Entity entity = packetplayinuseentity.a((World) worldserver);
1081 1084
1082 -@@ -775,22 +1451,72 @@
1085 +@@ -775,22 +1454,72 @@
1083 1086 EnumHand enumhand;
1084 1087 ItemStack itemstack;
1085 1088
1086 1089 + ItemStack itemInHand = this.player.b(packetplayinuseentity.b() == null ? EnumHand.MAIN_HAND : packetplayinuseentity.b()); // CraftBukkit
1087 1090 +
1088 1091 + if (packetplayinuseentity.a() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT
1089 1092 + || packetplayinuseentity.a() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT_AT) {
1090 1093 + // CraftBukkit start
1091 1094 + boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEAD && entity instanceof EntityInsentient;
1092 1095 + Item origItem = this.player.inventory.getItemInHand() == null ? null : this.player.inventory.getItemInHand().getItem();
1146 1149 this.player.attack(entity);
1147 1150 +
1148 1151 + // CraftBukkit start
1149 1152 + if (itemInHand != null && itemInHand.count <= -1) {
1150 1153 + this.player.updateInventory(this.player.activeContainer);
1151 1154 + }
1152 1155 + // CraftBukkit end
1153 1156 }
1154 1157 }
1155 1158 }
1156 -@@ -806,7 +1532,8 @@
1159 +@@ -806,7 +1535,8 @@
1157 1160 case 1:
1158 1161 if (this.player.viewingCredits) {
1159 1162 this.player.viewingCredits = false;
1160 1163 - this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, 0, true);
1161 1164 + // this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, 0, true);
1162 1165 + this.minecraftServer.getPlayerList().changeDimension(this.player, 0, PlayerTeleportEvent.TeleportCause.END_PORTAL); // CraftBukkit - reroute logic through custom portal management
1163 1166 } else {
1164 1167 if (this.player.getHealth() > 0.0F) {
1165 1168 return;
1166 -@@ -832,14 +1559,20 @@
1169 +@@ -832,14 +1562,20 @@
1167 1170
1168 1171 public void a(PacketPlayInCloseWindow packetplayinclosewindow) {
1169 1172 PlayerConnectionUtils.ensureMainThread(packetplayinclosewindow, this, this.player.x());
1170 1173 +
1171 1174 + if (this.player.dead) return; // CraftBukkit
1172 1175 + CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit
1173 1176 +
1174 1177 this.player.s();
1175 1178 }
1176 1179
1177 1180 public void a(PacketPlayInWindowClick packetplayinwindowclick) {
1178 1181 PlayerConnectionUtils.ensureMainThread(packetplayinwindowclick, this, this.player.x());
1179 1182 + if (this.player.dead) return; // CraftBukkit
1180 1183 this.player.resetIdleTimer();
1181 1184 if (this.player.activeContainer.windowId == packetplayinwindowclick.a() && this.player.activeContainer.c(this.player)) {
1182 1185 - if (this.player.isSpectator()) {
1183 1186 + boolean cancelled = this.player.isSpectator(); // CraftBukkit - see below if
1184 1187 + if (false/*this.player.isSpectator()*/) { // CraftBukkit
1185 1188 ArrayList arraylist = Lists.newArrayList();
1186 1189
1187 1190 for (int i = 0; i < this.player.activeContainer.c.size(); ++i) {
1188 -@@ -848,8 +1581,275 @@
1191 +@@ -848,8 +1584,275 @@
1189 1192
1190 1193 this.player.a(this.player.activeContainer, (List) arraylist);
1191 1194 } else {
1192 1195 - ItemStack itemstack = this.player.activeContainer.a(packetplayinwindowclick.b(), packetplayinwindowclick.c(), packetplayinwindowclick.f(), this.player);
1193 1196 + // CraftBukkit start - Call InventoryClickEvent
1194 1197 + if (packetplayinwindowclick.b() < -1 && packetplayinwindowclick.b() != -999) {
1195 1198 + return;
1196 1199 + }
1197 1200 +
1198 1201 + InventoryView inventory = this.player.activeContainer.getBukkitView();
1199 1202 + SlotType type = CraftInventoryView.getSlotType(inventory, packetplayinwindowclick.b());
1200 -
1203 ++
1201 1204 + InventoryClickEvent event;
1202 1205 + ClickType click = ClickType.UNKNOWN;
1203 1206 + InventoryAction action = InventoryAction.UNKNOWN;
1204 1207 +
1205 1208 + ItemStack itemstack = null;
1206 1209 +
1207 1210 + switch (packetplayinwindowclick.f()) {
1208 1211 + case PICKUP:
1209 1212 + if (packetplayinwindowclick.c() == 0) {
1210 1213 + click = ClickType.LEFT;
1385 1388 + if (packetplayinwindowclick.b() == 0 && top instanceof CraftingInventory) {
1386 1389 + org.bukkit.inventory.Recipe recipe = ((CraftingInventory) top).getRecipe();
1387 1390 + if (recipe != null) {
1388 1391 + if (click == ClickType.NUMBER_KEY) {
1389 1392 + event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.b(), click, action, packetplayinwindowclick.c());
1390 1393 + } else {
1391 1394 + event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.b(), click, action);
1392 1395 + }
1393 1396 + }
1394 1397 + }
1395 -+
1398 +
1396 1399 + event.setCancelled(cancelled);
1397 1400 + server.getPluginManager().callEvent(event);
1398 1401 +
1399 1402 + switch (event.getResult()) {
1400 1403 + case ALLOW:
1401 1404 + case DEFAULT:
1402 1405 + itemstack = this.player.activeContainer.a(packetplayinwindowclick.b(), packetplayinwindowclick.c(), packetplayinwindowclick.f(), this.player);
1403 1406 + break;
1404 1407 + case DENY:
1405 1408 + /* Needs enum constructor in InventoryAction
1455 1458 + if (event instanceof CraftItemEvent) {
1456 1459 + // Need to update the inventory on crafting to
1457 1460 + // correctly support custom recipes
1458 1461 + player.updateInventory(player.activeContainer);
1459 1462 + }
1460 1463 + }
1461 1464 + // CraftBukkit end
1462 1465 if (ItemStack.matches(packetplayinwindowclick.e(), itemstack)) {
1463 1466 this.player.playerConnection.sendPacket(new PacketPlayOutTransaction(packetplayinwindowclick.a(), packetplayinwindowclick.d(), true));
1464 1467 this.player.f = true;
1465 -@@ -878,6 +1878,7 @@
1468 +@@ -878,6 +1881,7 @@
1466 1469
1467 1470 public void a(PacketPlayInEnchantItem packetplayinenchantitem) {
1468 1471 PlayerConnectionUtils.ensureMainThread(packetplayinenchantitem, this, this.player.x());
1469 1472 + if (this.player.dead) return; // CraftBukkit
1470 1473 this.player.resetIdleTimer();
1471 1474 if (this.player.activeContainer.windowId == packetplayinenchantitem.a() && this.player.activeContainer.c(this.player) && !this.player.isSpectator()) {
1472 1475 this.player.activeContainer.a(this.player, packetplayinenchantitem.b());
1473 -@@ -911,8 +1912,48 @@
1476 +@@ -911,8 +1915,48 @@
1474 1477 }
1475 1478
1476 1479 boolean flag1 = packetplayinsetcreativeslot.a() >= 1 && packetplayinsetcreativeslot.a() <= 45;
1477 1480 - boolean flag2 = itemstack == null || itemstack.getItem() != null;
1478 1481 + // CraftBukkit - Add invalidItems check
1479 1482 + boolean flag2 = itemstack == null || itemstack.getItem() != null && !invalidItems.contains(Item.getId(itemstack.getItem()));
1480 1483 boolean flag3 = itemstack == null || itemstack.getData() >= 0 && itemstack.count <= 64 && itemstack.count > 0;
1481 1484 + // CraftBukkit start - Call click event
1482 1485 + if (flag || (flag1 && !ItemStack.matches(this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.a()).getItem(), packetplayinsetcreativeslot.getItemStack()))) { // Insist on valid slot
1483 1486 +
1513 1516 + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.defaultContainer.windowId, packetplayinsetcreativeslot.a(), this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.a()).getItem()));
1514 1517 + this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, null));
1515 1518 + }
1516 1519 + return;
1517 1520 + }
1518 1521 + }
1519 1522 + // CraftBukkit end
1520 1523
1521 1524 if (flag1 && flag2 && flag3) {
1522 1525 if (itemstack == null) {
1523 -@@ -936,6 +1977,7 @@
1526 +@@ -936,6 +1980,7 @@
1524 1527
1525 1528 public void a(PacketPlayInTransaction packetplayintransaction) {
1526 1529 PlayerConnectionUtils.ensureMainThread(packetplayintransaction, this, this.player.x());
1527 1530 + if (this.player.dead) return; // CraftBukkit
1528 1531 Short oshort = (Short) this.k.get(this.player.activeContainer.windowId);
1529 1532
1530 1533 if (oshort != null && packetplayintransaction.b() == oshort.shortValue() && this.player.activeContainer.windowId == packetplayintransaction.a() && !this.player.activeContainer.c(this.player) && !this.player.isSpectator()) {
1531 -@@ -946,6 +1988,7 @@
1534 +@@ -946,6 +1991,7 @@
1532 1535
1533 1536 public void a(PacketPlayInUpdateSign packetplayinupdatesign) {
1534 1537 PlayerConnectionUtils.ensureMainThread(packetplayinupdatesign, this, this.player.x());
1535 1538 + if (this.player.dead) return; // CraftBukkit
1536 1539 this.player.resetIdleTimer();
1537 1540 WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
1538 1541 BlockPosition blockposition = packetplayinupdatesign.a();
1539 -@@ -962,14 +2005,30 @@
1542 +@@ -962,14 +2008,30 @@
1540 1543
1541 1544 if (!tileentitysign.d() || tileentitysign.e() != this.player) {
1542 1545 this.minecraftServer.warning("Player " + this.player.getName() + " just tried to change non-editable sign");
1543 1546 + this.sendPacket(tileentity.getUpdatePacket()); // CraftBukkit
1544 1547 return;
1545 1548 }
1546 1549
1547 1550 String[] astring = packetplayinupdatesign.b();
1548 1551
1549 1552 + // CraftBukkit start
1561 1564 + this.server.getPluginManager().callEvent(event);
1562 1565 +
1563 1566 + if (!event.isCancelled()) {
1564 1567 + System.arraycopy(org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.getLines()), 0, tileentitysign.lines, 0, 4);
1565 1568 + tileentitysign.isEditable = false;
1566 1569 + }
1567 1570 + // CraftBukkit end
1568 1571
1569 1572 tileentitysign.update();
1570 1573 worldserver.notify(blockposition, iblockdata, iblockdata, 3);
1571 -@@ -992,11 +2051,27 @@
1574 +@@ -992,11 +2054,27 @@
1572 1575
1573 1576 public void a(PacketPlayInAbilities packetplayinabilities) {
1574 1577 PlayerConnectionUtils.ensureMainThread(packetplayinabilities, this, this.player.x());
1575 1578 - this.player.abilities.isFlying = packetplayinabilities.isFlying() && this.player.abilities.canFly;
1576 1579 + // CraftBukkit start
1577 1580 + if (this.player.abilities.canFly && this.player.abilities.isFlying != packetplayinabilities.isFlying()) {
1578 1581 + PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.server.getPlayer(this.player), packetplayinabilities.isFlying());
1579 1582 + this.server.getPluginManager().callEvent(event);
1580 1583 + if (!event.isCancelled()) {
1581 1584 + this.player.abilities.isFlying = packetplayinabilities.isFlying(); // Actually set the player's flying status
1590 1593 PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.x());
1591 1594 + // CraftBukkit start
1592 1595 + if (chatSpamField.addAndGet(this, 10) > 500 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) {
1593 1596 + this.disconnect("disconnect.spam");
1594 1597 + return;
1595 1598 + }
1596 1599 + // CraftBukkit end
1597 1600 ArrayList arraylist = Lists.newArrayList();
1598 1601 Iterator iterator = this.minecraftServer.tabCompleteCommand(this.player, packetplayintabcomplete.a(), packetplayintabcomplete.b(), packetplayintabcomplete.c()).iterator();
1599 1602
1600 -@@ -1040,10 +2115,13 @@
1603 +@@ -1040,10 +2118,13 @@
1601 1604 }
1602 1605
1603 1606 if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack.getItem() == itemstack1.getItem()) {
1604 1607 + itemstack1 = new ItemStack(Items.WRITABLE_BOOK); // CraftBukkit
1605 1608 itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8));
1606 1609 + CraftEventFactory.handleEditBookEvent(player, itemstack1); // CraftBukkit
1607 1610 }
1608 1611 } catch (Exception exception) {
1609 1612 PlayerConnection.LOGGER.error("Couldn\'t handle book info", exception);
1610 1613 + this.disconnect("Invalid book data!"); // CraftBukkit
1611 1614 }
1612 1615 } else {
1613 1616 String s1;
1614 -@@ -1067,6 +2145,7 @@
1617 +@@ -1067,6 +2148,7 @@
1615 1618 }
1616 1619
1617 1620 if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack1.getItem() == Items.WRITABLE_BOOK) {
1618 1621 + itemstack1 = new ItemStack(Items.WRITABLE_BOOK); // CraftBukkit
1619 1622 itemstack1.a("author", (NBTBase) (new NBTTagString(this.player.getName())));
1620 1623 itemstack1.a("title", (NBTBase) (new NBTTagString(itemstack.getTag().getString("title"))));
1621 1624 NBTTagList nbttaglist = itemstack.getTag().getList("pages", 8);
1622 -@@ -1081,9 +2160,11 @@
1625 +@@ -1081,9 +2163,11 @@
1623 1626
1624 1627 itemstack1.a("pages", (NBTBase) nbttaglist);
1625 1628 itemstack1.setItem(Items.WRITTEN_BOOK);
1626 1629 + CraftEventFactory.handleEditBookEvent(player, itemstack1); // CraftBukkit
1627 1630 }
1628 1631 } catch (Exception exception1) {
1629 1632 PlayerConnection.LOGGER.error("Couldn\'t sign book", exception1);
1630 1633 + this.disconnect("Invalid book data!"); // CraftBukkit
1631 1634 }
1632 1635 } else if ("MC|TrSel".equals(s)) {
1633 1636 try {
1634 -@@ -1095,6 +2176,7 @@
1637 +@@ -1095,6 +2179,7 @@
1635 1638 }
1636 1639 } catch (Exception exception2) {
1637 1640 PlayerConnection.LOGGER.error("Couldn\'t select trade", exception2);
1638 1641 + this.disconnect("Invalid trade data!"); // CraftBukkit
1639 1642 }
1640 1643 } else {
1641 1644 TileEntity tileentity;
1642 -@@ -1144,6 +2226,7 @@
1645 +@@ -1144,6 +2229,7 @@
1643 1646 }
1644 1647 } catch (Exception exception3) {
1645 1648 PlayerConnection.LOGGER.error("Couldn\'t set command block", exception3);
1646 1649 + this.disconnect("Invalid command data!"); // CraftBukkit
1647 1650 }
1648 1651 } else if ("MC|AutoCmd".equals(s)) {
1649 1652 if (!this.minecraftServer.getEnableCommandBlock()) {
1650 -@@ -1211,6 +2294,7 @@
1653 +@@ -1211,6 +2297,7 @@
1651 1654 }
1652 1655 } catch (Exception exception4) {
1653 1656 PlayerConnection.LOGGER.error("Couldn\'t set command block", exception4);
1654 1657 + this.disconnect("Invalid command data!"); // CraftBukkit
1655 1658 }
1656 1659 } else {
1657 1660 int k;
1658 -@@ -1234,6 +2318,7 @@
1661 +@@ -1234,6 +2321,7 @@
1659 1662 }
1660 1663 } catch (Exception exception5) {
1661 1664 PlayerConnection.LOGGER.error("Couldn\'t set beacon", exception5);
1662 1665 + this.disconnect("Invalid beacon data!"); // CraftBukkit
1663 1666 }
1664 1667 }
1665 1668 } else if ("MC|ItemName".equals(s)) {
1666 -@@ -1320,6 +2405,7 @@
1669 +@@ -1320,6 +2408,7 @@
1667 1670 }
1668 1671 } catch (Exception exception6) {
1669 1672 PlayerConnection.LOGGER.error("Couldn\'t set structure block", exception6);
1670 1673 + this.disconnect("Invalid structure data!"); // CraftBukkit
1671 1674 }
1672 1675 } else if ("MC|PickItem".equals(s)) {
1673 1676 packetdataserializer = packetplayincustompayload.b();
1674 -@@ -1332,14 +2418,37 @@
1677 +@@ -1332,14 +2421,37 @@
1675 1678 this.player.playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(this.player.inventory.itemInHandIndex));
1676 1679 } catch (Exception exception7) {
1677 1680 PlayerConnection.LOGGER.error("Couldn\'t pick item", exception7);
1678 1681 + this.disconnect("Invalid item data!"); // CraftBukkit
1679 1682 + }
1680 1683 + }
1681 1684 + // CraftBukkit start
1682 1685 + else if (packetplayincustompayload.a().equals("REGISTER")) {
1683 1686 + String channels = packetplayincustompayload.b().toString(com.google.common.base.Charsets.UTF_8);
1684 1687 + 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