diff options
author | Thinkofdeath <thinkofdeath@spigotmc.org> | 2014-11-26 08:32:16 +1100 |
---|---|---|
committer | md_5 <git@md-5.net> | 2014-11-28 17:16:30 +1100 |
commit | 24557bc2b37deb6a0edf497d547471832457b1dd (patch) | |
tree | c560572889a3b0b34964a0cddb35dc87fda3c914 /nms-patches/PlayerConnection.patch | |
parent | a4805dbd77da057cc1ea0bf344379bc6e53ca1f6 (diff) | |
download | craftbukkit-24557bc2b37deb6a0edf497d547471832457b1dd.tar craftbukkit-24557bc2b37deb6a0edf497d547471832457b1dd.tar.gz craftbukkit-24557bc2b37deb6a0edf497d547471832457b1dd.tar.lz craftbukkit-24557bc2b37deb6a0edf497d547471832457b1dd.tar.xz craftbukkit-24557bc2b37deb6a0edf497d547471832457b1dd.zip |
Update to Minecraft 1.8
For more information please see http://www.spigotmc.org/
Diffstat (limited to 'nms-patches/PlayerConnection.patch')
-rw-r--r-- | nms-patches/PlayerConnection.patch | 1458 |
1 files changed, 1458 insertions, 0 deletions
diff --git a/nms-patches/PlayerConnection.patch b/nms-patches/PlayerConnection.patch new file mode 100644 index 00000000..577557e6 --- /dev/null +++ b/nms-patches/PlayerConnection.patch @@ -0,0 +1,1458 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PlayerConnection.java 2014-11-27 08:59:46.853421177 +1100 ++++ src/main/java/net/minecraft/server/PlayerConnection.java 2014-11-27 08:42:10.148850918 +1100 +@@ -16,6 +16,48 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.util.concurrent.ExecutionException; ++import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; ++import java.util.HashSet; ++ ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.util.CraftChatMessage; ++import org.bukkit.craftbukkit.util.LazyPlayerSet; ++import org.bukkit.craftbukkit.util.Waitable; ++ ++import org.bukkit.Location; ++import org.bukkit.entity.Player; ++import org.bukkit.event.Event; ++import org.bukkit.event.block.Action; ++import org.bukkit.event.block.SignChangeEvent; ++import org.bukkit.event.inventory.ClickType; ++import org.bukkit.event.inventory.CraftItemEvent; ++import org.bukkit.event.inventory.InventoryAction; ++import org.bukkit.event.inventory.InventoryClickEvent; ++import org.bukkit.event.inventory.InventoryCreativeEvent; ++import org.bukkit.event.inventory.InventoryType.SlotType; ++import org.bukkit.event.player.AsyncPlayerChatEvent; ++import org.bukkit.event.player.PlayerAnimationEvent; ++import org.bukkit.event.player.PlayerChatEvent; ++import org.bukkit.event.player.PlayerCommandPreprocessEvent; ++import org.bukkit.event.player.PlayerInteractEntityEvent; ++import org.bukkit.event.player.PlayerInteractAtEntityEvent; ++import org.bukkit.event.player.PlayerItemHeldEvent; ++import org.bukkit.event.player.PlayerKickEvent; ++import org.bukkit.event.player.PlayerMoveEvent; ++import org.bukkit.event.player.PlayerTeleportEvent; ++import org.bukkit.event.player.PlayerToggleFlightEvent; ++import org.bukkit.event.player.PlayerToggleSneakEvent; ++import org.bukkit.event.player.PlayerToggleSprintEvent; ++import org.bukkit.inventory.CraftingInventory; ++import org.bukkit.inventory.InventoryView; ++import org.bukkit.util.NumberConversions; ++// CraftBukkit end ++ + public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerListBox { + + private static final Logger c = LogManager.getLogger(); +@@ -29,13 +71,17 @@ + private int i; + private long j; + private long k; +- private int chatThrottle; ++ // CraftBukkit start - multithreaded fields ++ private volatile int chatThrottle; ++ private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle"); ++ // CraftBukkit end + private int m; + private IntHashMap n = new IntHashMap(); + private double o; + private double p; + private double q; + public boolean checkMovement = true; ++ private boolean processedDisconnect; // CraftBukkit - added + + public PlayerConnection(MinecraftServer minecraftserver, NetworkManager networkmanager, EntityPlayer entityplayer) { + this.minecraftServer = minecraftserver; +@@ -43,7 +89,37 @@ + networkmanager.a((PacketListener) this); + this.player = entityplayer; + entityplayer.playerConnection = this; ++ ++ // CraftBukkit start - add fields and methods ++ this.server = minecraftserver.server; ++ } ++ ++ private final org.bukkit.craftbukkit.CraftServer server; ++ private int lastTick = MinecraftServer.currentTick; ++ private int lastDropTick = MinecraftServer.currentTick; ++ private int dropCount = 0; ++ private static final int SURVIVAL_PLACE_DISTANCE_SQUARED = 6 * 6; ++ private static final int CREATIVE_PLACE_DISTANCE_SQUARED = 7 * 7; ++ ++ // Get position of last block hit for BlockDamageLevel.STOPPED ++ private double lastPosX = Double.MAX_VALUE; ++ private double lastPosY = Double.MAX_VALUE; ++ private double lastPosZ = Double.MAX_VALUE; ++ private float lastPitch = Float.MAX_VALUE; ++ private float lastYaw = Float.MAX_VALUE; ++ private boolean justTeleported = false; ++ ++ // For the PacketPlayOutBlockPlace hack :( ++ Long lastPacket; ++ ++ // Store the last block right clicked and what type it was ++ private Item lastMaterial; ++ ++ public CraftPlayer getPlayer() { ++ return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity(); + } ++ 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. ++ // CraftBukkit end + + public void c() { + this.h = false; +@@ -57,9 +133,14 @@ + } + + this.minecraftServer.methodProfiler.b(); ++ // CraftBukkit start ++ for (int spam; (spam = this.chatThrottle) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1); ) ; ++ /* Use thread-safe field access instead + if (this.chatThrottle > 0) { + --this.chatThrottle; + } ++ */ ++ // CraftBukkit end + + if (this.m > 0) { + --this.m; +@@ -76,11 +157,27 @@ + } + + public void disconnect(String s) { ++ // CraftBukkit start - fire PlayerKickEvent ++ String leaveMessage = EnumChatFormat.YELLOW + this.player.getName() + " left the game."; ++ ++ PlayerKickEvent event = new PlayerKickEvent(this.server.getPlayer(this.player), s, leaveMessage); ++ ++ if (this.server.getServer().isRunning()) { ++ this.server.getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ // Do not kick the player ++ return; ++ } ++ // Send the possibly modified leave message ++ s = event.getReason(); ++ // CraftBukkit end + ChatComponentText chatcomponenttext = new ChatComponentText(s); + + this.networkManager.a(new PacketPlayOutKickDisconnect(chatcomponenttext), new PlayerConnectionFuture(this, chatcomponenttext), new GenericFutureListener[0]); + this.networkManager.k(); +- Futures.getUnchecked(this.minecraftServer.postToMainThread(new PlayerConnectionDisconnector(this))); ++ this.minecraftServer.postToMainThread(new PlayerConnectionDisconnector(this)); // CraftBukkit - Don't wait + } + + public void a(PacketPlayInSteerVehicle packetplayinsteervehicle) { +@@ -90,6 +187,13 @@ + + public void a(PacketPlayInFlying packetplayinflying) { + PlayerConnectionUtils.ensureMainThread(packetplayinflying, this, this.player.u()); ++ // CraftBukkit start - Check for NaN ++ if (Double.isNaN(packetplayinflying.x) || Double.isNaN(packetplayinflying.y) || Double.isNaN(packetplayinflying.z)) { ++ c.warn(player.getName() + " was caught trying to crash the server with an invalid position."); ++ getPlayer().kickPlayer("Nope!"); ++ return; ++ } ++ // CraftBukkit end + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + + this.h = true; +@@ -108,8 +212,65 @@ + this.checkMovement = true; + } + } ++ // CraftBukkit start - fire PlayerMoveEvent ++ Player player = this.getPlayer(); ++ Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location. ++ Location to = player.getLocation().clone(); // Start off the To location as the Players current location. ++ ++ // If the packet contains movement information then we update the To location with the correct XYZ. ++ if (packetplayinflying.hasPos && !(packetplayinflying.hasPos && packetplayinflying.y == -999.0D)) { ++ to.setX(packetplayinflying.x); ++ to.setY(packetplayinflying.y); ++ to.setZ(packetplayinflying.z); ++ } ++ ++ // If the packet contains look information then we update the To location with the correct Yaw & Pitch. ++ if (packetplayinflying.hasLook) { ++ to.setYaw(packetplayinflying.yaw); ++ to.setPitch(packetplayinflying.pitch); ++ } ++ ++ // Prevent 40 event-calls for less than a single pixel of movement >.> ++ double delta = Math.pow(this.lastPosX - to.getX(), 2) + Math.pow(this.lastPosY - to.getY(), 2) + Math.pow(this.lastPosZ - to.getZ(), 2); ++ float deltaAngle = Math.abs(this.lastYaw - to.getYaw()) + Math.abs(this.lastPitch - to.getPitch()); ++ ++ if ((delta > 1f / 256 || deltaAngle > 10f) && (this.checkMovement && !this.player.dead)) { ++ this.lastPosX = to.getX(); ++ this.lastPosY = to.getY(); ++ this.lastPosZ = to.getZ(); ++ this.lastYaw = to.getYaw(); ++ this.lastPitch = to.getPitch(); ++ ++ // Skip the first time we do this ++ if (from.getX() != Double.MAX_VALUE) { ++ PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); ++ this.server.getPluginManager().callEvent(event); ++ ++ // If the event is cancelled we move the player back to their old location. ++ if (event.isCancelled()) { ++ this.player.playerConnection.sendPacket(new PacketPlayOutPosition(from.getX(), from.getY() + 1.6200000047683716D, from.getZ(), from.getYaw(), from.getPitch(), Collections.emptySet())); ++ return; ++ } ++ ++ /* If a Plugin has changed the To destination then we teleport the Player ++ there to avoid any 'Moved wrongly' or 'Moved too quickly' errors. ++ We only do this if the Event was not cancelled. */ ++ if (!to.equals(event.getTo()) && !event.isCancelled()) { ++ this.player.getBukkitEntity().teleport(event.getTo(), PlayerTeleportEvent.TeleportCause.UNKNOWN); ++ return; ++ } ++ ++ /* Check to see if the Players Location has some how changed during the call of the event. ++ This can happen due to a plugin teleporting the player instead of using .setTo() */ ++ if (!from.equals(this.getPlayer().getLocation()) && this.justTeleported) { ++ this.justTeleported = false; ++ return; ++ } ++ } ++ } + +- if (this.checkMovement) { ++ if (this.checkMovement && !this.player.dead) { ++ // CraftBukkit end + this.f = this.e; + double d7; + double d8; +@@ -203,12 +364,14 @@ + double d11 = d7 - this.player.locX; + double d12 = d8 - this.player.locY; + double d13 = d9 - this.player.locZ; +- double d14 = Math.min(Math.abs(d11), Math.abs(this.player.motX)); +- double d15 = Math.min(Math.abs(d12), Math.abs(this.player.motY)); +- double d16 = Math.min(Math.abs(d13), Math.abs(this.player.motZ)); ++ // CraftBukkit start - min to max ++ double d14 = Math.max(Math.abs(d11), Math.abs(this.player.motX)); ++ double d15 = Math.max(Math.abs(d12), Math.abs(this.player.motY)); ++ double d16 = Math.max(Math.abs(d13), Math.abs(this.player.motZ)); ++ // CraftBukkit end + double d17 = d14 * d14 + d15 * d15 + d16 * d16; + +- if (d17 > 100.0D && (!this.minecraftServer.S() || !this.minecraftServer.R().equals(this.player.getName()))) { ++ if (d17 > 100.0D && this.checkMovement && (!this.minecraftServer.S() || !this.minecraftServer.R().equals(this.player.getName()))) { // CraftBukkit - Added this.checkMovement condition to solve this check being triggered by teleports + PlayerConnection.c.warn(this.player.getName() + " moved too quickly! " + d11 + "," + d12 + "," + d13 + " (" + d14 + ", " + d15 + ", " + d16 + ")"); + this.a(this.o, this.p, this.q, this.player.yaw, this.player.pitch); + return; +@@ -281,6 +444,49 @@ + } + + public void a(double d0, double d1, double d2, float f, float f1, Set set) { ++ // CraftBukkit start - Delegate to teleport(Location) ++ Player player = this.getPlayer(); ++ Location from = player.getLocation(); ++ Location to = new Location(this.getPlayer().getWorld(), d0, d1, d2, f, f1); ++ PlayerTeleportEvent event = new PlayerTeleportEvent(player, from, to, PlayerTeleportEvent.TeleportCause.UNKNOWN); ++ this.server.getPluginManager().callEvent(event); ++ ++ from = event.getFrom(); ++ to = event.isCancelled() ? from : event.getTo(); ++ ++ this.teleport(to, set); ++ } ++ ++ public void teleport(Location dest) { ++ teleport(dest, Collections.emptySet()); ++ } ++ ++ public void teleport(Location dest, Set set) { ++ double d0, d1, d2; ++ float f, f1; ++ ++ d0 = dest.getX(); ++ d1 = dest.getY(); ++ d2 = dest.getZ(); ++ f = dest.getYaw(); ++ f1 = dest.getPitch(); ++ ++ // TODO: make sure this is the best way to address this. ++ if (Float.isNaN(f)) { ++ f = 0; ++ } ++ ++ if (Float.isNaN(f1)) { ++ f1 = 0; ++ } ++ ++ this.lastPosX = d0; ++ this.lastPosY = d1; ++ this.lastPosZ = d2; ++ this.lastYaw = f; ++ this.lastPitch = f1; ++ this.justTeleported = true; ++ // CraftBukkit end + this.checkMovement = false; + this.o = d0; + this.p = d1; +@@ -314,32 +520,49 @@ + + public void a(PacketPlayInBlockDig packetplayinblockdig) { + PlayerConnectionUtils.ensureMainThread(packetplayinblockdig, this, this.player.u()); ++ if (this.player.dead) return; // CraftBukkit + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + BlockPosition blockposition = packetplayinblockdig.a(); + + this.player.z(); ++ // CraftBukkit start + switch (SwitchHelperCommandActionType.a[packetplayinblockdig.c().ordinal()]) { +- case 1: ++ case 1: // DROP_ITEM + if (!this.player.v()) { ++ // limit how quickly items can be dropped ++ // If the ticks aren't the same then the count starts from 0 and we update the lastDropTick. ++ if (this.lastDropTick != MinecraftServer.currentTick) { ++ this.dropCount = 0; ++ this.lastDropTick = MinecraftServer.currentTick; ++ } else { ++ // Else we increment the drop count and check the amount. ++ this.dropCount++; ++ if (this.dropCount >= 20) { ++ this.c.warn(this.player.getName() + " dropped their items too quickly!"); ++ this.disconnect("You dropped your items too quickly (Hacking?)"); ++ return; ++ } ++ } ++ // CraftBukkit end + this.player.a(false); + } + + return; + +- case 2: ++ case 2: // DROP_ALL_ITEMS + if (!this.player.v()) { + this.player.a(true); + } + + return; + +- case 3: ++ case 3: // RELEASE_USE_ITEM + this.player.bT(); + return; + +- case 4: +- case 5: +- case 6: ++ case 4: // START_DESTROY_BLOCK ++ case 5: // ABORT_DESTROY_BLOCK ++ case 6: // STOP_DESTROY_BLOCK + double d0 = this.player.locX - ((double) blockposition.getX() + 0.5D); + double d1 = this.player.locY - ((double) blockposition.getY() + 0.5D) + 1.5D; + double d2 = this.player.locZ - ((double) blockposition.getZ() + 0.5D); +@@ -354,7 +577,15 @@ + if (!this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.af().a(blockposition)) { + this.player.playerInteractManager.a(blockposition, packetplayinblockdig.b()); + } else { ++ // CraftBukkit start - fire PlayerInteractEvent ++ CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, packetplayinblockdig.b(), this.player.inventory.getItemInHand()); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition)); ++ // Update any tile entity data for this block ++ TileEntity tileentity = worldserver.getTileEntity(blockposition); ++ if (tileentity != null) { ++ this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); ++ } ++ // CraftBukkit end + } + } else { + if (packetplayinblockdig.c() == EnumPlayerDigType.STOP_DESTROY_BLOCK) { +@@ -374,11 +605,38 @@ + default: + throw new IllegalArgumentException("Invalid player action"); + } ++ // CraftBukkit end + } + + public void a(PacketPlayInBlockPlace packetplayinblockplace) { + PlayerConnectionUtils.ensureMainThread(packetplayinblockplace, this, this.player.u()); + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); ++ ++ // CraftBukkit start ++ if (this.player.dead) return; ++ ++ // This is a horrible hack needed because the client sends 2 packets on 'right mouse click' ++ // aimed at a block. We shouldn't need to get the second packet if the data is handled ++ // but we cannot know what the client will do, so we might still get it ++ // ++ // If the time between packets is small enough, and the 'signature' similar, we discard the ++ // second one. This sadly has to remain until Mojang makes their packets saner. :( ++ // -- Grum ++ if (packetplayinblockplace.getFace() == 255) { ++ if (packetplayinblockplace.getItemStack() != null && packetplayinblockplace.getItemStack().getItem() == this.lastMaterial && this.lastPacket != null && packetplayinblockplace.timestamp - this.lastPacket < 100) { ++ this.lastPacket = null; ++ return; ++ } ++ } else { ++ this.lastMaterial = packetplayinblockplace.getItemStack() == null ? null : packetplayinblockplace.getItemStack().getItem(); ++ this.lastPacket = packetplayinblockplace.timestamp; ++ } ++ // CraftBukkit - if rightclick decremented the item, always send the update packet. */ ++ // this is not here for CraftBukkit's own functionality; rather it is to fix ++ // a notch bug where the item doesn't update correctly. ++ boolean always = false; ++ // CraftBukkit end ++ + ItemStack itemstack = this.player.inventory.getItemInHand(); + boolean flag = false; + BlockPosition blockposition = packetplayinblockplace.a(); +@@ -390,7 +648,18 @@ + return; + } + +- this.player.playerInteractManager.useItem(this.player, worldserver, itemstack); ++ // CraftBukkit start ++ int itemstackAmount = itemstack.count; ++ org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemstack); ++ if (event.useItemInHand() != Event.Result.DENY) { ++ this.player.playerInteractManager.useItem(this.player, this.player.world, itemstack); ++ } ++ ++ // CraftBukkit - notch decrements the counter by 1 in the above method with food, ++ // snowballs and so forth, but he does it in a place that doesn't cause the ++ // inventory update packet to get sent ++ always = (itemstack.count != itemstackAmount) || itemstack.getItem() == Item.getItemOf(Blocks.WATERLILY); ++ // CraftBukkit end + } else if (blockposition.getY() >= this.minecraftServer.getMaxBuildHeight() - 1 && (enumdirection == EnumDirection.UP || blockposition.getY() >= this.minecraftServer.getMaxBuildHeight())) { + ChatMessage chatmessage = new ChatMessage("build.tooHigh", new Object[] { Integer.valueOf(this.minecraftServer.getMaxBuildHeight())}); + +@@ -398,9 +667,21 @@ + this.player.playerConnection.sendPacket(new PacketPlayOutChat(chatmessage)); + flag = true; + } else { ++ // CraftBukkit start - Check if we can actually do something over this large a distance ++ Location eyeLoc = this.getPlayer().getEyeLocation(); ++ double reachDistance = NumberConversions.square(eyeLoc.getX() - blockposition.getX()) + NumberConversions.square(eyeLoc.getY() - blockposition.getY()) + NumberConversions.square(eyeLoc.getZ() - blockposition.getZ()); ++ if (reachDistance > (this.getPlayer().getGameMode() == org.bukkit.GameMode.CREATIVE ? CREATIVE_PLACE_DISTANCE_SQUARED : SURVIVAL_PLACE_DISTANCE_SQUARED)) { ++ return; ++ } ++ ++ if (!worldserver.af().a(blockposition)) { ++ return; ++ } ++ + if (this.checkMovement && 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.af().a(blockposition)) { +- this.player.playerInteractManager.interact(this.player, worldserver, itemstack, blockposition, enumdirection, packetplayinblockplace.d(), packetplayinblockplace.e(), packetplayinblockplace.f()); ++ always = !this.player.playerInteractManager.interact(this.player, worldserver, itemstack, blockposition, enumdirection, packetplayinblockplace.d(), packetplayinblockplace.e(), packetplayinblockplace.f()); + } ++ // CraftBukkit end + + flag = true; + } +@@ -423,7 +704,8 @@ + + this.player.activeContainer.b(); + this.player.g = false; +- if (!ItemStack.matches(this.player.inventory.getItemInHand(), packetplayinblockplace.getItemStack())) { ++ // CraftBukkit - TODO CHECK IF NEEDED -- new if structure might not need 'always'. Kept it in for now, but may be able to remove in future ++ if (!ItemStack.matches(this.player.inventory.getItemInHand(), packetplayinblockplace.getItemStack()) || always) { + this.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, slot.rawSlotIndex, this.player.inventory.getItemInHand())); + } + } +@@ -437,8 +719,8 @@ + WorldServer[] aworldserver = this.minecraftServer.worldServer; + int i = aworldserver.length; + +- for (int j = 0; j < i; ++j) { +- WorldServer worldserver = aworldserver[j]; ++ // CraftBukkit - use the worlds array list ++ for (WorldServer worldserver : minecraftServer.worlds) { + + if (worldserver != null) { + entity = packetplayinspectate.a(worldserver); +@@ -455,6 +737,7 @@ + WorldServer worldserver1 = this.player.u(); + WorldServer worldserver2 = (WorldServer) entity.world; + ++ /* CraftBukkit start - replace with bukkit handling for multi-world + this.player.dimension = entity.dimension; + this.sendPacket(new PacketPlayOutRespawn(this.player.dimension, worldserver1.getDifficulty(), worldserver1.getWorldData().getType(), this.player.playerInteractManager.getGameMode())); + worldserver1.removeEntity(this.player); +@@ -472,6 +755,9 @@ + this.player.playerInteractManager.a(worldserver2); + this.minecraftServer.getPlayerList().b(this.player, worldserver2); + this.minecraftServer.getPlayerList().updateClient(this.player); ++ */ ++ this.player.getBukkitEntity().teleport(entity.getBukkitEntity()); ++ // CraftBukkit end + } else { + this.player.enderTeleportTo(entity.locX, entity.locY, entity.locZ); + } +@@ -483,14 +769,29 @@ + public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) {} + + public void a(IChatBaseComponent ichatbasecomponent) { +- PlayerConnection.c.info(this.player.getName() + " lost connection: " + ichatbasecomponent); ++ // CraftBukkit start - Rarely it would send a disconnect line twice ++ if (this.processedDisconnect) { ++ return; ++ } else { ++ this.processedDisconnect = true; ++ } ++ // CraftBukkit end ++ c.info(this.player.getName() + " lost connection: " + ichatbasecomponent.c()); // CraftBukkit - Don't toString the component + this.minecraftServer.aF(); ++ // CraftBukkit start - Replace vanilla quit message handling with our own. ++ /* + ChatMessage chatmessage = new ChatMessage("multiplayer.player.left", new Object[] { this.player.getScoreboardDisplayName()}); + + chatmessage.getChatModifier().setColor(EnumChatFormat.YELLOW); + this.minecraftServer.getPlayerList().sendMessage(chatmessage); ++ */ ++ + this.player.q(); +- this.minecraftServer.getPlayerList().disconnect(this.player); ++ String quitMessage = this.minecraftServer.getPlayerList().disconnect(this.player); ++ if ((quitMessage != null) && (quitMessage.length() > 0)) { ++ this.minecraftServer.getPlayerList().sendMessage(CraftChatMessage.fromString(quitMessage)); ++ } ++ // CraftBukkit end + if (this.minecraftServer.S() && this.player.getName().equals(this.minecraftServer.R())) { + PlayerConnection.c.info("Stopping singleplayer server as player logged out"); + this.minecraftServer.safeShutdown(); +@@ -511,6 +812,15 @@ + return; + } + } ++ ++ // CraftBukkit start ++ if (packet == null) { ++ return; ++ } else if (packet instanceof PacketPlayOutSpawnPosition) { ++ PacketPlayOutSpawnPosition packet6 = (PacketPlayOutSpawnPosition) packet; ++ this.player.compassTarget = new Location(this.getPlayer().getWorld(), packet6.position.getX(), packet6.position.getY(), packet6.position.getZ()); ++ } ++ // CraftBukkit end + + try { + this.networkManager.handle(packet); +@@ -524,18 +834,34 @@ + } + + public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) { ++ // CraftBukkit start ++ if (this.player.dead) return; + PlayerConnectionUtils.ensureMainThread(packetplayinhelditemslot, this, this.player.u()); + if (packetplayinhelditemslot.a() >= 0 && packetplayinhelditemslot.a() < PlayerInventory.getHotbarSize()) { ++ PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getPlayer(), this.player.inventory.itemInHandIndex, packetplayinhelditemslot.a()); ++ this.server.getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ this.sendPacket(new PacketPlayOutHeldItemSlot(this.player.inventory.itemInHandIndex)); ++ this.player.z(); // RENAME ++ return; ++ } ++ // CraftBukkit end + this.player.inventory.itemInHandIndex = packetplayinhelditemslot.a(); + this.player.z(); + } else { + PlayerConnection.c.warn(this.player.getName() + " tried to set an invalid carried item"); ++ this.disconnect("Nope!"); // CraftBukkit + } + } + + public void a(PacketPlayInChat packetplayinchat) { +- PlayerConnectionUtils.ensureMainThread(packetplayinchat, this, this.player.u()); +- if (this.player.getChatFlags() == EnumChatVisibility.HIDDEN) { ++ // CraftBukkit start - async chat ++ boolean isSync = packetplayinchat.a().startsWith("/"); ++ if (packetplayinchat.a().startsWith("/")) { ++ PlayerConnectionUtils.ensureMainThread(packetplayinchat, this, this.player.u()); ++ } ++ // CraftBukkit end ++ if (this.player.dead || this.player.getChatFlags() == EnumChatVisibility.HIDDEN) { // CraftBukkit - dead men tell no tales + ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]); + + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); +@@ -548,39 +874,248 @@ + + for (int i = 0; i < s.length(); ++i) { + if (!SharedConstants.isAllowedChatCharacter(s.charAt(i))) { +- this.disconnect("Illegal characters in chat"); ++ // CraftBukkit start - threadsafety ++ if (!isSync) { ++ Waitable waitable = new Waitable() { ++ @Override ++ protected Object evaluate() { ++ PlayerConnection.this.disconnect("Illegal characters in chat"); ++ return null; ++ } ++ }; ++ ++ this.minecraftServer.processQueue.add(waitable); ++ ++ try { ++ waitable.get(); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); ++ } catch (ExecutionException e) { ++ throw new RuntimeException(e); ++ } ++ } else { ++ this.disconnect("Illegal characters in chat"); ++ } ++ // CraftBukkit end + return; + } + } + +- if (s.startsWith("/")) { +- this.handleCommand(s); ++ // CraftBukkit start ++ if (isSync) { ++ try { ++ this.minecraftServer.server.playerCommandState = true; ++ this.handleCommand(s); ++ } finally { ++ this.minecraftServer.server.playerCommandState = false; ++ } ++ } else if (s.isEmpty()) { ++ c.warn(this.player.getName() + " tried to send an empty message"); ++ } else if (getPlayer().isConversing()) { ++ getPlayer().acceptConversationInput(s); ++ } else if (this.player.getChatFlags() == EnumChatVisibility.SYSTEM) { // Re-add "Command Only" flag check ++ ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]); ++ ++ chatmessage.getChatModifier().setColor(EnumChatFormat.RED); ++ this.sendPacket(new PacketPlayOutChat(chatmessage)); ++ } else if (true) { ++ this.chat(s, true); ++ // CraftBukkit end - the below is for reference. :) + } else { + ChatMessage chatmessage1 = new ChatMessage("chat.type.text", new Object[] { this.player.getScoreboardDisplayName(), s}); + + this.minecraftServer.getPlayerList().sendMessage(chatmessage1, false); + } + +- this.chatThrottle += 20; +- if (this.chatThrottle > 200 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { +- this.disconnect("disconnect.spam"); +- } ++ // CraftBukkit start - replaced with thread safe throttle ++ // this.chatThrottle += 20; ++ if (chatSpamField.addAndGet(this, 20) > 200 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { ++ if (!isSync) { ++ Waitable waitable = new Waitable() { ++ @Override ++ protected Object evaluate() { ++ PlayerConnection.this.disconnect("disconnect.spam"); ++ return null; ++ } ++ }; ++ ++ this.minecraftServer.processQueue.add(waitable); + ++ try { ++ waitable.get(); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); ++ } catch (ExecutionException e) { ++ throw new RuntimeException(e); ++ } ++ } else { ++ this.disconnect("disconnect.spam"); ++ } ++ // CraftBukkit end ++ } + } + } ++ ++ // CraftBukkit start - add method ++ public void chat(String s, boolean async) { ++ if (s.isEmpty() || this.player.getChatFlags() == EnumChatVisibility.HIDDEN) { ++ return; ++ } ++ ++ if (!async && s.startsWith("/")) { ++ this.handleCommand(s); ++ } else if (this.player.getChatFlags() == EnumChatVisibility.SYSTEM) { ++ // Do nothing, this is coming from a plugin ++ } else { ++ Player player = this.getPlayer(); ++ AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(async, player, s, new LazyPlayerSet()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (PlayerChatEvent.getHandlerList().getRegisteredListeners().length != 0) { ++ // Evil plugins still listening to deprecated event ++ final PlayerChatEvent queueEvent = new PlayerChatEvent(player, event.getMessage(), event.getFormat(), event.getRecipients()); ++ queueEvent.setCancelled(event.isCancelled()); ++ Waitable waitable = new Waitable() { ++ @Override ++ protected Object evaluate() { ++ org.bukkit.Bukkit.getPluginManager().callEvent(queueEvent); ++ ++ if (queueEvent.isCancelled()) { ++ return null; ++ } ++ ++ String message = String.format(queueEvent.getFormat(), queueEvent.getPlayer().getDisplayName(), queueEvent.getMessage()); ++ PlayerConnection.this.minecraftServer.console.sendMessage(message); ++ if (((LazyPlayerSet) queueEvent.getRecipients()).isLazy()) { ++ for (Object player : PlayerConnection.this.minecraftServer.getPlayerList().players) { ++ ((EntityPlayer) player).sendMessage(CraftChatMessage.fromString(message)); ++ } ++ } else { ++ for (Player player : queueEvent.getRecipients()) { ++ player.sendMessage(message); ++ } ++ } ++ return null; ++ }}; ++ if (async) { ++ minecraftServer.processQueue.add(waitable); ++ } else { ++ waitable.run(); ++ } ++ try { ++ waitable.get(); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on! ++ } catch (ExecutionException e) { ++ throw new RuntimeException("Exception processing chat event", e.getCause()); ++ } ++ } else { ++ if (event.isCancelled()) { ++ return; ++ } ++ ++ s = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage()); ++ minecraftServer.console.sendMessage(s); ++ if (((LazyPlayerSet) event.getRecipients()).isLazy()) { ++ for (Object recipient : minecraftServer.getPlayerList().players) { ++ ((EntityPlayer) recipient).sendMessage(CraftChatMessage.fromString(s)); ++ } ++ } else { ++ for (Player recipient : event.getRecipients()) { ++ recipient.sendMessage(s); ++ } ++ } ++ } ++ } ++ } ++ // CraftBukkit end + + private void handleCommand(String s) { +- this.minecraftServer.getCommandHandler().a(this.player, s); ++ // CraftBukkit start - whole method ++ this.c.info(this.player.getName() + " issued server command: " + s); ++ ++ CraftPlayer player = this.getPlayer(); ++ ++ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, s, new LazyPlayerSet()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ ++ try { ++ if (this.server.dispatchCommand(event.getPlayer(), event.getMessage().substring(1))) { ++ return; ++ } ++ } catch (org.bukkit.command.CommandException ex) { ++ player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command"); ++ java.util.logging.Logger.getLogger(PlayerConnection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); ++ return; ++ } ++ // this.minecraftServer.getCommandHandler().a(this.player, s); ++ // CraftBukkit end + } + + public void a(PacketPlayInArmAnimation packetplayinarmanimation) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayinarmanimation, this, this.player.u()); + this.player.z(); ++ // CraftBukkit start - Raytrace to look for 'rogue armswings' ++ float f = 1.0F; ++ float f1 = this.player.lastPitch + (this.player.pitch - this.player.lastPitch) * f; ++ float f2 = this.player.lastYaw + (this.player.yaw - this.player.lastYaw) * f; ++ double d0 = this.player.lastX + (this.player.locX - this.player.lastX) * (double) f; ++ double d1 = this.player.lastY + (this.player.locY - this.player.lastY) * (double) f + 1.62D - (double) this.player.getHeadHeight(); ++ double d2 = this.player.lastZ + (this.player.locZ - this.player.lastZ) * (double) f; ++ Vec3D vec3d = new Vec3D(d0, d1, d2); ++ ++ float f3 = MathHelper.cos(-f2 * 0.017453292F - 3.1415927F); ++ float f4 = MathHelper.sin(-f2 * 0.017453292F - 3.1415927F); ++ float f5 = -MathHelper.cos(-f1 * 0.017453292F); ++ float f6 = MathHelper.sin(-f1 * 0.017453292F); ++ float f7 = f4 * f5; ++ float f8 = f3 * f5; ++ double d3 = 5.0D; ++ Vec3D vec3d1 = vec3d.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3); ++ MovingObjectPosition movingobjectposition = this.player.world.rayTrace(vec3d, vec3d1, false); ++ ++ if (movingobjectposition == null || movingobjectposition.type != EnumMovingObjectType.BLOCK) { ++ CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_AIR, this.player.inventory.getItemInHand()); ++ } ++ ++ // Arm swing animation ++ PlayerAnimationEvent event = new PlayerAnimationEvent(this.getPlayer()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) return; ++ // CraftBukkit end + this.player.bv(); + } + + public void a(PacketPlayInEntityAction packetplayinentityaction) { + PlayerConnectionUtils.ensureMainThread(packetplayinentityaction, this, this.player.u()); ++ // CraftBukkit start ++ if (this.player.dead) return; ++ switch (packetplayinentityaction.b()) { ++ case START_SNEAKING: ++ case STOP_SNEAKING: ++ PlayerToggleSneakEvent event = new PlayerToggleSneakEvent(this.getPlayer(), packetplayinentityaction.b() == EnumPlayerAction.START_SNEAKING); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ break; ++ case START_SPRINTING: ++ case STOP_SPRINTING: ++ PlayerToggleSprintEvent e2 = new PlayerToggleSprintEvent(this.getPlayer(), packetplayinentityaction.b() == EnumPlayerAction.START_SPRINTING); ++ this.server.getPluginManager().callEvent(e2); ++ ++ if (e2.isCancelled()) { ++ return; ++ } ++ break; ++ } + this.player.z(); + switch (SwitchHelperCommandActionType.b[packetplayinentityaction.b().ordinal()]) { + case 1: +@@ -601,7 +1136,7 @@ + + case 5: + this.player.a(false, true, true); +- this.checkMovement = false; ++ // this.checkMovement = false; // CraftBukkit - this is handled in teleport + break; + + case 6: +@@ -623,6 +1158,7 @@ + } + + public void a(PacketPlayInUseEntity packetplayinuseentity) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayinuseentity, this, this.player.u()); + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + Entity entity = packetplayinuseentity.a((World) worldserver); +@@ -637,18 +1173,72 @@ + } + + if (this.player.h(entity) < d0) { ++ ItemStack itemInHand = this.player.inventory.getItemInHand(); // CraftBukkit ++ ++ if (packetplayinuseentity.a() == EnumEntityUseAction.INTERACT ++ || packetplayinuseentity.a() == EnumEntityUseAction.INTERACT_AT) { ++ // CraftBukkit start ++ boolean triggerTagUpdate = itemInHand != null && itemInHand.getItem() == Items.NAME_TAG && entity instanceof EntityInsentient; ++ boolean triggerChestUpdate = itemInHand != null && itemInHand.getItem() == Item.getItemOf(Blocks.CHEST) && entity instanceof EntityHorse; ++ boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEAD && entity instanceof EntityInsentient; ++ PlayerInteractEntityEvent event; ++ if (packetplayinuseentity.a() == EnumEntityUseAction.INTERACT) { ++ event = new PlayerInteractEntityEvent((Player) this.getPlayer(), entity.getBukkitEntity()); ++ } else { ++ Vec3D target = packetplayinuseentity.b(); ++ event = new PlayerInteractAtEntityEvent((Player) this.getPlayer(), entity.getBukkitEntity(), new org.bukkit.util.Vector(target.a, target.b, target.c)); ++ } ++ this.server.getPluginManager().callEvent(event); ++ ++ if (triggerLeashUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != Items.LEAD)) { ++ // Refresh the current leash state ++ this.sendPacket(new PacketPlayOutAttachEntity(1, entity, ((EntityInsentient) entity).getLeashHolder())); ++ } ++ ++ if (triggerTagUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != Items.NAME_TAG)) { ++ // Refresh the current entity metadata ++ this.sendPacket(new PacketPlayOutEntityMetadata(entity.getId(), entity.datawatcher, true)); ++ } ++ if (triggerChestUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != Item.getItemOf(Blocks.CHEST))) { ++ this.sendPacket(new PacketPlayOutEntityMetadata(entity.getId(), entity.datawatcher, true)); ++ } ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end ++ } ++ + if (packetplayinuseentity.a() == EnumEntityUseAction.INTERACT) { + this.player.u(entity); ++ ++ // CraftBukkit start ++ if (itemInHand != null && itemInHand.count <= -1) { ++ this.player.updateInventory(this.player.activeContainer); ++ } ++ // CraftBukkit end + } else if (packetplayinuseentity.a() == EnumEntityUseAction.INTERACT_AT) { + entity.a((EntityHuman) this.player, packetplayinuseentity.b()); ++ ++ // CraftBukkit start ++ if (itemInHand != null && itemInHand.count <= -1) { ++ this.player.updateInventory(this.player.activeContainer); ++ } ++ // CraftBukkit end + } else if (packetplayinuseentity.a() == EnumEntityUseAction.ATTACK) { +- if (entity instanceof EntityItem || entity instanceof EntityExperienceOrb || entity instanceof EntityArrow || entity == this.player) { ++ if (entity instanceof EntityItem || entity instanceof EntityExperienceOrb || entity instanceof EntityArrow || (entity == this.player && !player.v())) { // CraftBukkit, RENAME + this.disconnect("Attempting to attack an invalid entity"); + this.minecraftServer.warning("Player " + this.player.getName() + " tried to attack an invalid entity"); + return; + } + + this.player.attack(entity); ++ ++ // CraftBukkit start ++ if (itemInHand != null && itemInHand.count <= -1) { ++ this.player.updateInventory(this.player.activeContainer); ++ } ++ // CraftBukkit end + } + } + } +@@ -663,7 +1253,8 @@ + switch (SwitchHelperCommandActionType.c[enumclientcommand.ordinal()]) { + case 1: + if (this.player.viewingCredits) { +- this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, 0, true); ++ // this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, 0, true); ++ this.minecraftServer.getPlayerList().changeDimension(this.player, 0, PlayerTeleportEvent.TeleportCause.END_PORTAL); // CraftBukkit - reroute logic through custom portal management + } else if (this.player.u().getWorldData().isHardcore()) { + if (this.minecraftServer.S() && this.player.getName().equals(this.minecraftServer.R())) { + this.player.playerConnection.disconnect("You have died. Game over, man, it\'s game over!"); +@@ -694,11 +1285,17 @@ + } + + public void a(PacketPlayInCloseWindow packetplayinclosewindow) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayinclosewindow, this, this.player.u()); ++ ++ CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit ++ + this.player.p(); + } + + public void a(PacketPlayInWindowClick packetplayinwindowclick) { ++ if (this.player.dead) return; // CraftBukkit ++ + PlayerConnectionUtils.ensureMainThread(packetplayinwindowclick, this, this.player.u()); + this.player.z(); + if (this.player.activeContainer.windowId == packetplayinwindowclick.a() && this.player.activeContainer.c(this.player)) { +@@ -711,7 +1308,263 @@ + + this.player.a(this.player.activeContainer, (List) arraylist); + } else { +- ItemStack itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.b(), packetplayinwindowclick.c(), packetplayinwindowclick.f(), this.player); ++ // ItemStack itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.b(), packetplayinwindowclick.c(), packetplayinwindowclick.f(), this.player); ++ // CraftBukkit start - Call InventoryClickEvent ++ if (packetplayinwindowclick.b() < -1 && packetplayinwindowclick.b() != -999) { ++ return; ++ } ++ ++ InventoryView inventory = this.player.activeContainer.getBukkitView(); ++ SlotType type = CraftInventoryView.getSlotType(inventory, packetplayinwindowclick.b()); ++ ++ InventoryClickEvent event = null; ++ ClickType click = ClickType.UNKNOWN; ++ InventoryAction action = InventoryAction.UNKNOWN; ++ ++ ItemStack itemstack = null; ++ ++ if (packetplayinwindowclick.b() == -1) { ++ type = SlotType.OUTSIDE; // override ++ click = packetplayinwindowclick.c() == 0 ? ClickType.WINDOW_BORDER_LEFT : ClickType.WINDOW_BORDER_RIGHT; ++ action = InventoryAction.NOTHING; ++ } else if (packetplayinwindowclick.f() == 0) { ++ if (packetplayinwindowclick.c() == 0) { ++ click = ClickType.LEFT; ++ } else if (packetplayinwindowclick.c() == 1) { ++ click = ClickType.RIGHT; ++ } ++ if (packetplayinwindowclick.c() == 0 || packetplayinwindowclick.c() == 1) { ++ action = InventoryAction.NOTHING; // Don't want to repeat ourselves ++ if (packetplayinwindowclick.b() == -999) { ++ if (player.inventory.getCarried() != null) { ++ action = packetplayinwindowclick.c() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR; ++ } ++ } else { ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null) { ++ ItemStack clickedItem = slot.getItem(); ++ ItemStack cursor = player.inventory.getCarried(); ++ if (clickedItem == null) { ++ if (cursor != null) { ++ action = packetplayinwindowclick.c() == 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_ONE; ++ } ++ } else if (slot.isAllowed(player)) { ++ if (cursor == null) { ++ action = packetplayinwindowclick.c() == 0 ? InventoryAction.PICKUP_ALL : InventoryAction.PICKUP_HALF; ++ } else if (slot.isAllowed(cursor)) { ++ if (clickedItem.doMaterialsMatch(cursor) && ItemStack.equals(clickedItem, cursor)) { ++ int toPlace = packetplayinwindowclick.c() == 0 ? cursor.count : 1; ++ toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.count); ++ toPlace = Math.min(toPlace, slot.inventory.getMaxStackSize() - clickedItem.count); ++ if (toPlace == 1) { ++ action = InventoryAction.PLACE_ONE; ++ } else if (toPlace == cursor.count) { ++ action = InventoryAction.PLACE_ALL; ++ } else if (toPlace < 0) { ++ action = toPlace != -1 ? InventoryAction.PICKUP_SOME : InventoryAction.PICKUP_ONE; // this happens with oversized stacks ++ } else if (toPlace != 0) { ++ action = InventoryAction.PLACE_SOME; ++ } ++ } else if (cursor.count <= slot.getMaxStackSize()) { ++ action = InventoryAction.SWAP_WITH_CURSOR; ++ } ++ } else if (cursor.getItem() == clickedItem.getItem() && (!cursor.usesData() || cursor.getData() == clickedItem.getData()) && ItemStack.equals(cursor, clickedItem)) { ++ if (clickedItem.count >= 0) { ++ if (clickedItem.count + cursor.count <= cursor.getMaxStackSize()) { ++ // As of 1.5, this is result slots only ++ action = InventoryAction.PICKUP_ALL; ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ } else if (packetplayinwindowclick.f() == 1) { ++ if (packetplayinwindowclick.c() == 0) { ++ click = ClickType.SHIFT_LEFT; ++ } else if (packetplayinwindowclick.c() == 1) { ++ click = ClickType.SHIFT_RIGHT; ++ } ++ if (packetplayinwindowclick.c() == 0 || packetplayinwindowclick.c() == 1) { ++ if (packetplayinwindowclick.b() < 0) { ++ action = InventoryAction.NOTHING; ++ } else { ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null && slot.isAllowed(this.player) && slot.hasItem()) { ++ action = InventoryAction.MOVE_TO_OTHER_INVENTORY; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ } ++ } else if (packetplayinwindowclick.f() == 2) { ++ if (packetplayinwindowclick.c() >= 0 && packetplayinwindowclick.c() < 9) { ++ click = ClickType.NUMBER_KEY; ++ Slot clickedSlot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (clickedSlot.isAllowed(player)) { ++ ItemStack hotbar = this.player.inventory.getItem(packetplayinwindowclick.c()); ++ boolean canCleanSwap = hotbar == null || (clickedSlot.inventory == player.inventory && clickedSlot.isAllowed(hotbar)); // the slot will accept the hotbar item ++ if (clickedSlot.hasItem()) { ++ if (canCleanSwap) { ++ action = InventoryAction.HOTBAR_SWAP; ++ } else { ++ int firstEmptySlot = player.inventory.getFirstEmptySlotIndex(); ++ if (firstEmptySlot > -1) { ++ action = InventoryAction.HOTBAR_MOVE_AND_READD; ++ } else { ++ action = InventoryAction.NOTHING; // This is not sane! Mojang: You should test for other slots of same type ++ } ++ } ++ } else if (!clickedSlot.hasItem() && hotbar != null && clickedSlot.isAllowed(hotbar)) { ++ action = InventoryAction.HOTBAR_SWAP; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ // Special constructor for number key ++ event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.b(), click, action, packetplayinwindowclick.c()); ++ } ++ } else if (packetplayinwindowclick.f() == 3) { ++ if (packetplayinwindowclick.c() == 2) { ++ click = ClickType.MIDDLE; ++ if (packetplayinwindowclick.b() == -999) { ++ action = InventoryAction.NOTHING; ++ } else { ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null && slot.hasItem() && player.abilities.canInstantlyBuild && player.inventory.getCarried() == null) { ++ action = InventoryAction.CLONE_STACK; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ } else { ++ click = ClickType.UNKNOWN; ++ action = InventoryAction.UNKNOWN; ++ } ++ } else if (packetplayinwindowclick.f() == 4) { ++ if (packetplayinwindowclick.b() >= 0) { ++ if (packetplayinwindowclick.c() == 0) { ++ click = ClickType.DROP; ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null && slot.hasItem() && slot.isAllowed(player) && slot.getItem() != null && slot.getItem().getItem() != Item.getItemOf(Blocks.AIR)) { ++ action = InventoryAction.DROP_ONE_SLOT; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } else if (packetplayinwindowclick.c() == 1) { ++ click = ClickType.CONTROL_DROP; ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null && slot.hasItem() && slot.isAllowed(player) && slot.getItem() != null && slot.getItem().getItem() != Item.getItemOf(Blocks.AIR)) { ++ action = InventoryAction.DROP_ALL_SLOT; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ } else { ++ // Sane default (because this happens when they are holding nothing. Don't ask why.) ++ click = ClickType.LEFT; ++ if (packetplayinwindowclick.c() == 1) { ++ click = ClickType.RIGHT; ++ } ++ action = InventoryAction.NOTHING; ++ } ++ } else if (packetplayinwindowclick.f() == 5) { ++ itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.b(), packetplayinwindowclick.c(), 5, this.player); ++ } else if (packetplayinwindowclick.f() == 6) { ++ click = ClickType.DOUBLE_CLICK; ++ action = InventoryAction.NOTHING; ++ if (packetplayinwindowclick.b() >= 0 && this.player.inventory.getCarried() != null) { ++ ItemStack cursor = this.player.inventory.getCarried(); ++ action = InventoryAction.NOTHING; ++ // Quick check for if we have any of the item ++ if (inventory.getTopInventory().contains(org.bukkit.Material.getMaterial(Item.getId(cursor.getItem()))) || inventory.getBottomInventory().contains(org.bukkit.Material.getMaterial(Item.getId(cursor.getItem())))) { ++ action = InventoryAction.COLLECT_TO_CURSOR; ++ } ++ } ++ } ++ // TODO check on updates ++ ++ if (packetplayinwindowclick.f() != 5) { ++ if (click == ClickType.NUMBER_KEY) { ++ event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.b(), click, action, packetplayinwindowclick.c()); ++ } else { ++ event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.b(), click, action); ++ } ++ ++ org.bukkit.inventory.Inventory top = inventory.getTopInventory(); ++ if (packetplayinwindowclick.b() == 0 && top instanceof CraftingInventory) { ++ org.bukkit.inventory.Recipe recipe = ((CraftingInventory) top).getRecipe(); ++ if (recipe != null) { ++ if (click == ClickType.NUMBER_KEY) { ++ event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.b(), click, action, packetplayinwindowclick.c()); ++ } else { ++ event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.b(), click, action); ++ } ++ } ++ } ++ ++ server.getPluginManager().callEvent(event); ++ ++ switch (event.getResult()) { ++ case ALLOW: ++ case DEFAULT: ++ itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.b(), packetplayinwindowclick.c(), packetplayinwindowclick.f(), this.player); ++ break; ++ case DENY: ++ /* Needs enum constructor in InventoryAction ++ if (action.modifiesOtherSlots()) { ++ ++ } else { ++ if (action.modifiesCursor()) { ++ this.player.playerConnection.sendPacket(new Packet103SetSlot(-1, -1, this.player.inventory.getCarried())); ++ } ++ if (action.modifiesClicked()) { ++ this.player.playerConnection.sendPacket(new Packet103SetSlot(this.player.activeContainer.windowId, packet102windowclick.slot, this.player.activeContainer.getSlot(packet102windowclick.slot).getItem())); ++ } ++ }*/ ++ switch (action) { ++ // Modified other slots ++ case PICKUP_ALL: ++ case MOVE_TO_OTHER_INVENTORY: ++ case HOTBAR_MOVE_AND_READD: ++ case HOTBAR_SWAP: ++ case COLLECT_TO_CURSOR: ++ case UNKNOWN: ++ this.player.updateInventory(this.player.activeContainer); ++ break; ++ // Modified cursor and clicked ++ case PICKUP_SOME: ++ case PICKUP_HALF: ++ case PICKUP_ONE: ++ case PLACE_ALL: ++ case PLACE_SOME: ++ case PLACE_ONE: ++ case SWAP_WITH_CURSOR: ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.player.inventory.getCarried())); ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, packetplayinwindowclick.b(), this.player.activeContainer.getSlot(packetplayinwindowclick.b()).getItem())); ++ break; ++ // Modified clicked only ++ case DROP_ALL_SLOT: ++ case DROP_ONE_SLOT: ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, packetplayinwindowclick.b(), this.player.activeContainer.getSlot(packetplayinwindowclick.b()).getItem())); ++ break; ++ // Modified cursor only ++ case DROP_ALL_CURSOR: ++ case DROP_ONE_CURSOR: ++ case CLONE_STACK: ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.player.inventory.getCarried())); ++ break; ++ // Nothing ++ case NOTHING: ++ break; ++ } ++ return; ++ } ++ } ++ // CraftBukkit end + + if (ItemStack.matches(packetplayinwindowclick.e(), itemstack)) { + this.player.playerConnection.sendPacket(new PacketPlayOutTransaction(packetplayinwindowclick.a(), packetplayinwindowclick.d(), true)); +@@ -772,8 +1625,50 @@ + } + + boolean flag1 = packetplayinsetcreativeslot.a() >= 1 && packetplayinsetcreativeslot.a() < 36 + PlayerInventory.getHotbarSize(); +- boolean flag2 = itemstack == null || itemstack.getItem() != null; ++ // CraftBukkit - Add invalidItems check ++ boolean flag2 = itemstack == null || itemstack.getItem() != null && !invalidItems.contains(Item.getId(itemstack.getItem())); + boolean flag3 = itemstack == null || itemstack.getData() >= 0 && itemstack.count <= 64 && itemstack.count > 0; ++ ++ ++ // CraftBukkit start - Call click event ++ if (flag || (flag1 && !ItemStack.matches(this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.a()).getItem(), packetplayinsetcreativeslot.getItemStack()))) { // Insist on valid slot ++ ++ org.bukkit.entity.HumanEntity player = this.player.getBukkitEntity(); ++ InventoryView inventory = new CraftInventoryView(player, player.getInventory(), this.player.defaultContainer); ++ org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packetplayinsetcreativeslot.getItemStack()); ++ ++ SlotType type = SlotType.QUICKBAR; ++ if (flag) { ++ type = SlotType.OUTSIDE; ++ } else if (packetplayinsetcreativeslot.a() < 36) { ++ if (packetplayinsetcreativeslot.a() >= 5 && packetplayinsetcreativeslot.a() < 9) { ++ type = SlotType.ARMOR; ++ } else { ++ type = SlotType.CONTAINER; ++ } ++ } ++ InventoryCreativeEvent event = new InventoryCreativeEvent(inventory, type, flag ? -999 : packetplayinsetcreativeslot.a(), item); ++ server.getPluginManager().callEvent(event); ++ ++ itemstack = CraftItemStack.asNMSCopy(event.getCursor()); ++ ++ switch (event.getResult()) { ++ case ALLOW: ++ // Plugin cleared the id / stacksize checks ++ flag2 = flag3 = true; ++ break; ++ case DEFAULT: ++ break; ++ case DENY: ++ // Reset the slot ++ if (packetplayinsetcreativeslot.a() >= 0) { ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.defaultContainer.windowId, packetplayinsetcreativeslot.a(), this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.a()).getItem())); ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, null)); ++ } ++ return; ++ } ++ } ++ // CraftBukkit end + + if (flag1 && flag2 && flag3) { + if (itemstack == null) { +@@ -796,6 +1691,7 @@ + } + + public void a(PacketPlayInTransaction packetplayintransaction) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayintransaction, this, this.player.u()); + Short oshort = (Short) this.n.get(this.player.activeContainer.windowId); + +@@ -806,6 +1702,7 @@ + } + + public void a(PacketPlayInUpdateSign packetplayinupdatesign) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayinupdatesign, this, this.player.u()); + this.player.z(); + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); +@@ -822,10 +1719,24 @@ + + if (!tileentitysign.b() || tileentitysign.c() != this.player) { + this.minecraftServer.warning("Player " + this.player.getName() + " just tried to change non-editable sign"); ++ this.sendPacket(new PacketPlayOutUpdateSign(tileentity.world, packetplayinupdatesign.a(), tileentitysign.lines)); // CraftBukkit + return; + } + +- System.arraycopy(packetplayinupdatesign.b(), 0, tileentitysign.lines, 0, 4); ++ // CraftBukkit start ++ Player player = this.server.getPlayer(this.player); ++ int x = packetplayinupdatesign.a().getX(); ++ int y = packetplayinupdatesign.a().getY(); ++ int z = packetplayinupdatesign.a().getZ(); ++ SignChangeEvent event = new SignChangeEvent((org.bukkit.craftbukkit.block.CraftBlock) player.getWorld().getBlockAt(x, y, z), this.server.getPlayer(this.player), org.bukkit.craftbukkit.block.CraftSign.revertComponents(packetplayinupdatesign.b())); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ System.arraycopy(org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.getLines()), 0, tileentitysign.lines, 0, 4); ++ tileentitysign.isEditable = false; ++ } ++ // System.arraycopy(packetplayinupdatesign.b(), 0, tileentitysign.lines, 0, 4); ++ // CraftBukkit end + tileentitysign.update(); + worldserver.notify(blockposition); + } +@@ -847,11 +1758,28 @@ + + public void a(PacketPlayInAbilities packetplayinabilities) { + PlayerConnectionUtils.ensureMainThread(packetplayinabilities, this, this.player.u()); +- this.player.abilities.isFlying = packetplayinabilities.isFlying() && this.player.abilities.canFly; ++ // CraftBukkit start ++ if (this.player.abilities.canFly && this.player.abilities.isFlying != packetplayinabilities.isFlying()) { ++ PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.server.getPlayer(this.player), packetplayinabilities.isFlying()); ++ this.server.getPluginManager().callEvent(event); ++ if (!event.isCancelled()) { ++ this.player.abilities.isFlying = packetplayinabilities.isFlying(); // Actually set the player's flying status ++ } else { ++ this.player.updateAbilities(); // Tell the player their ability was reverted ++ } ++ } ++ // CraftBukkit end + } + + public void a(PacketPlayInTabComplete packetplayintabcomplete) { + PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.u()); ++ // CraftBukkit start ++ if (chatSpamField.addAndGet(this, 20) > 200 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { ++ this.disconnect("disconnect.spam"); ++ return; ++ } ++ // CraftBukkit end ++ + ArrayList arraylist = Lists.newArrayList(); + Iterator iterator = this.minecraftServer.tabCompleteCommand(this.player, packetplayintabcomplete.a(), packetplayintabcomplete.b()).iterator(); + +@@ -891,13 +1819,15 @@ + itemstack1 = this.player.inventory.getItemInHand(); + if (itemstack1 != null) { + if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack.getItem() == itemstack1.getItem()) { +- itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8)); ++ // itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8)); ++ CraftEventFactory.handleEditBookEvent(player, itemstack); // CraftBukkit + } + + return; + } + } catch (Exception exception) { + PlayerConnection.c.error("Couldn\'t handle book info", exception); ++ this.disconnect("Invalid book data!"); // CraftBukkit + return; + } finally { + packetdataserializer.release(); +@@ -909,27 +1839,31 @@ + + try { + itemstack = packetdataserializer.i(); +- if (itemstack == null) { +- return; +- } ++ if (itemstack != null) { ++ if (!ItemWrittenBook.b(itemstack.getTag())) { ++ throw new IOException("Invalid book tag!"); ++ } + +- if (!ItemWrittenBook.b(itemstack.getTag())) { +- throw new IOException("Invalid book tag!"); +- } ++ itemstack1 = this.player.inventory.getItemInHand(); ++ if (itemstack1 == null) { ++ return; ++ } + +- itemstack1 = this.player.inventory.getItemInHand(); +- if (itemstack1 != null) { + if (itemstack.getItem() == Items.WRITTEN_BOOK && itemstack1.getItem() == Items.WRITABLE_BOOK) { +- itemstack1.a("author", (NBTBase) (new NBTTagString(this.player.getName()))); +- itemstack1.a("title", (NBTBase) (new NBTTagString(itemstack.getTag().getString("title")))); +- itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8)); +- itemstack1.setItem(Items.WRITTEN_BOOK); ++ // CraftBukkit start ++ // itemstack1.a("author", (NBTBase) (new NBTTagString(this.player.getName()))); ++ // itemstack1.a("title", (NBTBase) (new NBTTagString(itemstack.getTag().getString("title")))); ++ // itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8)); ++ // itemstack1.setItem(Items.WRITTEN_BOOK); ++ CraftEventFactory.handleEditBookEvent(player, itemstack); ++ // CraftBukkit end + } + + return; + } + } catch (Exception exception1) { + PlayerConnection.c.error("Couldn\'t sign book", exception1); ++ this.disconnect("Invalid book data!"); // CraftBukkit + return; + } finally { + packetdataserializer.release(); +@@ -946,6 +1880,7 @@ + } + } catch (Exception exception2) { + PlayerConnection.c.error("Couldn\'t select trade", exception2); ++ this.disconnect("Invalid trade data!"); // CraftBukkit + } + } else if ("MC|AdvCdm".equals(packetplayincustompayload.a())) { + if (!this.minecraftServer.getEnableCommandBlock()) { +@@ -986,6 +1921,7 @@ + } + } catch (Exception exception3) { + PlayerConnection.c.error("Couldn\'t set command block", exception3); ++ this.disconnect("Invalid CommandBlock data!"); // CraftBukkit + } finally { + packetdataserializer.release(); + } +@@ -1011,6 +1947,7 @@ + } + } catch (Exception exception4) { + PlayerConnection.c.error("Couldn\'t set beacon", exception4); ++ this.disconnect("Invalid beacon data!"); // CraftBukkit + } + } + } else if ("MC|ItemName".equals(packetplayincustompayload.a()) && this.player.activeContainer instanceof ContainerAnvil) { +@@ -1026,6 +1963,27 @@ + containeranvil.a(""); + } + } ++ // CraftBukkit start ++ else if (packetplayincustompayload.a().equals("REGISTER")) { ++ String channels = packetplayincustompayload.b().toString(com.google.common.base.Charsets.UTF_8); ++ for (String channel : channels.split("\0")) { ++ getPlayer().addChannel(channel); ++ } ++ } else if (packetplayincustompayload.a().equals("UNREGISTER")) { ++ String channels = packetplayincustompayload.b().toString(com.google.common.base.Charsets.UTF_8); ++ for (String channel : channels.split("\0")) { ++ getPlayer().removeChannel(channel); ++ } ++ } else { ++ byte[] data = new byte[packetplayincustompayload.b().readableBytes()]; ++ packetplayincustompayload.b().readBytes(data); ++ server.getMessenger().dispatchIncomingMessage(player.getBukkitEntity(), packetplayincustompayload.a(), data); ++ } ++ // CraftBukkit end ++ } + ++ // CraftBukkit start - Add "isDisconnected" method ++ public final boolean isDisconnected() { ++ return !this.player.joining && !NetworkManager.a(this.networkManager).config().isAutoRead(); + } + } |