From 19a594ed1689467aba50bb9ace86c75a0ab2ee69 Mon Sep 17 00:00:00 2001
From: kashike <kashike@vq.lc>
Date: Wed, 15 Aug 2018 01:26:09 -0700
Subject: [PATCH] Allow disabling armour stand ticking


diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
index f06bb3ae19..a5b4f99901 100644
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
@@ -391,4 +391,10 @@ public class PaperWorldConfig {
             log("Bed Search Radius: " + bedSearchRadius);
         }
     }
+
+    public boolean armorStandTick = true;
+    private void armorStandTick() {
+        this.armorStandTick = this.getBoolean("armor-stands-tick", this.armorStandTick);
+        log("ArmorStand ticking is " + (this.armorStandTick ? "enabled" : "disabled") + " by default");
+    }
 }
diff --git a/src/main/java/net/minecraft/server/EntityArmorStand.java b/src/main/java/net/minecraft/server/EntityArmorStand.java
index b5b7631919..9157dace14 100644
--- a/src/main/java/net/minecraft/server/EntityArmorStand.java
+++ b/src/main/java/net/minecraft/server/EntityArmorStand.java
@@ -44,6 +44,11 @@ public class EntityArmorStand extends EntityLiving {
     public Vector3f leftLegPose;
     public Vector3f rightLegPose;
     public boolean canMove = true; // Paper
+    // Paper start - Allow ArmorStands not to tick
+    public boolean canTick = true;
+    private boolean noTickPoseDirty = false;
+    private boolean noTickEquipmentDirty = false;
+    // Paper end
 
     public EntityArmorStand(EntityTypes<? extends EntityArmorStand> entitytypes, World world) {
         super(entitytypes, world);
@@ -55,6 +60,7 @@ public class EntityArmorStand extends EntityLiving {
         this.rightArmPose = EntityArmorStand.bx;
         this.leftLegPose = EntityArmorStand.by;
         this.rightLegPose = EntityArmorStand.bz;
+        if (world != null) this.canTick = world.paperConfig.armorStandTick; // Paper - armour stand ticking
         this.K = 0.0F;
     }
 
@@ -135,6 +141,7 @@ public class EntityArmorStand extends EntityLiving {
                 this.armorItems.set(enumitemslot.b(), itemstack);
         }
 
+        this.noTickEquipmentDirty = true; // Paper - Allow equipment to be updated even when tick disabled
     }
 
     @Override
@@ -215,6 +222,7 @@ public class EntityArmorStand extends EntityLiving {
         }
 
         nbttagcompound.set("Pose", this.B());
+        nbttagcompound.setBoolean("Paper.CanTick", this.canTick); // Paper - persist no tick setting
     }
 
     @Override
@@ -246,6 +254,11 @@ public class EntityArmorStand extends EntityLiving {
         this.setBasePlate(nbttagcompound.getBoolean("NoBasePlate"));
         this.setMarker(nbttagcompound.getBoolean("Marker"));
         this.noclip = !this.A();
+        // Paper start - persist no tick
+        if (nbttagcompound.hasKey("Paper.CanTick")) {
+            this.canTick = nbttagcompound.getBoolean("Paper.CanTick");
+        }
+        // Paper end
         NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Pose");
 
         this.g(nbttagcompound1);
@@ -584,7 +597,29 @@ public class EntityArmorStand extends EntityLiving {
 
     @Override
     public void tick() {
+        // Paper start
+        if (!this.canTick) {
+            if (this.noTickPoseDirty) {
+                this.noTickPoseDirty = false;
+                this.updatePose();
+            }
+
+            if (this.noTickEquipmentDirty) {
+                this.noTickEquipmentDirty = false;
+                this.updateEntityEquipment();
+            }
+
+            return;
+        }
+        // Paper end
+
         super.tick();
+        // Paper start - Split into separate method
+        updatePose();
+    }
+
+    public void updatePose() {
+        // Paper end
         Vector3f vector3f = (Vector3f) this.datawatcher.get(EntityArmorStand.c);
 
         if (!this.headPose.equals(vector3f)) {
@@ -700,31 +735,37 @@ public class EntityArmorStand extends EntityLiving {
     public void setHeadPose(Vector3f vector3f) {
         this.headPose = vector3f;
         this.datawatcher.set(EntityArmorStand.c, vector3f);
+        this.noTickPoseDirty = true; // Paper - Allow updates when not ticking
     }
 
     public void setBodyPose(Vector3f vector3f) {
         this.bodyPose = vector3f;
         this.datawatcher.set(EntityArmorStand.d, vector3f);
+        this.noTickPoseDirty = true; // Paper - Allow updates when not ticking
     }
 
     public void setLeftArmPose(Vector3f vector3f) {
         this.leftArmPose = vector3f;
         this.datawatcher.set(EntityArmorStand.e, vector3f);
+        this.noTickPoseDirty = true; // Paper - Allow updates when not ticking
     }
 
     public void setRightArmPose(Vector3f vector3f) {
         this.rightArmPose = vector3f;
         this.datawatcher.set(EntityArmorStand.f, vector3f);
+        this.noTickPoseDirty = true; // Paper - Allow updates when not ticking
     }
 
     public void setLeftLegPose(Vector3f vector3f) {
         this.leftLegPose = vector3f;
         this.datawatcher.set(EntityArmorStand.g, vector3f);
+        this.noTickPoseDirty = true; // Paper - Allow updates when not ticking
     }
 
     public void setRightLegPose(Vector3f vector3f) {
         this.rightLegPose = vector3f;
         this.datawatcher.set(EntityArmorStand.bs, vector3f);
+        this.noTickPoseDirty = true; // Paper - Allow updates when not ticking
     }
 
     public Vector3f r() {
diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java
index d2dddf1c72..1dd8475877 100644
--- a/src/main/java/net/minecraft/server/EntityLiving.java
+++ b/src/main/java/net/minecraft/server/EntityLiving.java
@@ -2265,52 +2265,7 @@ public abstract class EntityLiving extends Entity {
                 }
             }
 
-            EnumItemSlot[] aenumitemslot = EnumItemSlot.values();
-            int j = aenumitemslot.length;
-
-            for (int k = 0; k < j; ++k) {
-                EnumItemSlot enumitemslot = aenumitemslot[k];
-                ItemStack itemstack;
-
-                switch (enumitemslot.a()) {
-                    case HAND:
-                        itemstack = (ItemStack) this.bw.get(enumitemslot.b());
-                        break;
-                    case ARMOR:
-                        itemstack = (ItemStack) this.bx.get(enumitemslot.b());
-                        break;
-                    default:
-                        continue;
-                }
-
-                ItemStack itemstack1 = this.getEquipment(enumitemslot);
-
-                if (!ItemStack.matches(itemstack1, itemstack)) {
-                    // Paper start - PlayerArmorChangeEvent
-                    if (this instanceof EntityPlayer && enumitemslot.getType() == EnumItemSlot.Function.ARMOR) {
-                        final org.bukkit.inventory.ItemStack oldItem = CraftItemStack.asBukkitCopy(itemstack);
-                        final org.bukkit.inventory.ItemStack newItem = CraftItemStack.asBukkitCopy(itemstack1);
-                        new PlayerArmorChangeEvent((Player) this.getBukkitEntity(), PlayerArmorChangeEvent.SlotType.valueOf(enumitemslot.name()), oldItem, newItem).callEvent();
-                    }
-                    // Paper end
-                    ((WorldServer) this.world).getChunkProvider().broadcast(this, new PacketPlayOutEntityEquipment(this.getId(), enumitemslot, itemstack1));
-                    if (!itemstack.isEmpty()) {
-                        this.getAttributeMap().a(itemstack.a(enumitemslot));
-                    }
-
-                    if (!itemstack1.isEmpty()) {
-                        this.getAttributeMap().b(itemstack1.a(enumitemslot));
-                    }
-
-                    switch (enumitemslot.a()) {
-                        case HAND:
-                            this.bw.set(enumitemslot.b(), itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack());
-                            break;
-                        case ARMOR:
-                            this.bx.set(enumitemslot.b(), itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack());
-                    }
-                }
-            }
+            updateEntityEquipment(); // Paper - split into own method
 
             if (this.ticksLived % 20 == 0) {
                 this.getCombatTracker().g();
@@ -2411,6 +2366,57 @@ public abstract class EntityLiving extends Entity {
         }
     }
 
+    // Paper start - split into own method from above
+    public void updateEntityEquipment() {
+        EnumItemSlot[] aenumitemslot = EnumItemSlot.values();
+        int j = aenumitemslot.length;
+
+        for (int k = 0; k < j; ++k) {
+            EnumItemSlot enumitemslot = aenumitemslot[k];
+            ItemStack itemstack;
+
+            switch (enumitemslot.a()) {
+                case HAND:
+                    itemstack = (ItemStack) this.bw.get(enumitemslot.b());
+                    break;
+                case ARMOR:
+                    itemstack = (ItemStack) this.bx.get(enumitemslot.b());
+                    break;
+                default:
+                    continue;
+            }
+
+            ItemStack itemstack1 = this.getEquipment(enumitemslot);
+
+            if (!ItemStack.matches(itemstack1, itemstack)) {
+                // Paper start - PlayerArmorChangeEvent
+                if (this instanceof EntityPlayer && enumitemslot.getType() == EnumItemSlot.Function.ARMOR) {
+                    final org.bukkit.inventory.ItemStack oldItem = CraftItemStack.asBukkitCopy(itemstack);
+                    final org.bukkit.inventory.ItemStack newItem = CraftItemStack.asBukkitCopy(itemstack1);
+                    new PlayerArmorChangeEvent((Player) this.getBukkitEntity(), PlayerArmorChangeEvent.SlotType.valueOf(enumitemslot.name()), oldItem, newItem).callEvent();
+                }
+                // Paper end
+                ((WorldServer) this.world).getChunkProvider().broadcast(this, new PacketPlayOutEntityEquipment(this.getId(), enumitemslot, itemstack1));
+                if (!itemstack.isEmpty()) {
+                    this.getAttributeMap().a(itemstack.a(enumitemslot));
+                }
+
+                if (!itemstack1.isEmpty()) {
+                    this.getAttributeMap().b(itemstack1.a(enumitemslot));
+                }
+
+                switch (enumitemslot.a()) {
+                    case HAND:
+                        this.bw.set(enumitemslot.b(), itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack());
+                        break;
+                    case ARMOR:
+                        this.bx.set(enumitemslot.b(), itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack());
+                }
+            }
+        }
+    }
+    // Paper end
+
     protected float e(float f, float f1) {
         float f2 = MathHelper.g(f - this.aK);
 
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftArmorStand.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftArmorStand.java
index 9f5c3b92e3..07ce93f17c 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftArmorStand.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftArmorStand.java
@@ -297,5 +297,15 @@ public class CraftArmorStand extends CraftLivingEntity implements ArmorStand {
     public boolean isSlotDisabled(org.bukkit.inventory.EquipmentSlot slot) {
         return getHandle().isSlotDisabled(org.bukkit.craftbukkit.CraftEquipmentSlot.getNMS(slot));
     }
+
+    @Override
+    public boolean canTick() {
+        return this.getHandle().canTick;
+    }
+
+    @Override
+    public void setCanTick(final boolean tick) {
+        this.getHandle().canTick = tick;
+    }
     // Paper end
 }
-- 
2.21.0

