From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Sat, 22 Feb 2020 15:54:08 -0600
Subject: [PATCH] Item entity immunities


diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index c82ae84190273609b0f5f533eb41a0e5363c16bf..a2352906df37efe6b3203fe299963f4f530625aa 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -2323,7 +2323,7 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
 
     public class TrackedEntity {
 
-        final ServerEntity serverEntity;
+        public final ServerEntity serverEntity; // Purpur -> package -> public
         final Entity entity;
         private final int range;
         SectionPos lastSectionPos;
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
index 61c4d3ec612195f31bcce7e5d46d012767c5e231..0c22c9b40cbdcd0098a83dec0804abc5882c4b54 100644
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
@@ -68,7 +68,7 @@ public class ServerEntity {
     private boolean wasRiding;
     private boolean wasOnGround;
     // CraftBukkit start
-    final Set<ServerPlayerConnection> trackedPlayers; // Paper - private -> package
+    public final Set<ServerPlayerConnection> trackedPlayers; // Paper - private -> package // Purpur - package -> public
 
     public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer<Packet<?>> consumer, Set<ServerPlayerConnection> trackedPlayers) {
         this.trackedPlayers = trackedPlayers;
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index 2dac4bc346eb1dde1f8d92c3e5b9648b2c433617..d3c09c1b3951cf11d6eff798d3b3e5a85c879b20 100644
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
@@ -53,6 +53,12 @@ public class ItemEntity extends Entity {
     public final float bobOffs;
     private int lastTick = MinecraftServer.currentTick - 1; // CraftBukkit
     public boolean canMobPickup = true; // Paper
+    // Purpur start
+    public boolean immuneToCactus = false;
+    public boolean immuneToExplosion = false;
+    public boolean immuneToFire = false;
+    public boolean immuneToLightning = false;
+    // Purpur end
 
     public ItemEntity(EntityType<? extends ItemEntity> type, Level world) {
         super(type, world);
@@ -328,6 +334,16 @@ public class ItemEntity extends Entity {
             return false;
         } else if (!this.getItem().getItem().canBeHurtBy(source)) {
             return false;
+        // Purpur start
+        } else if (
+                (immuneToCactus && source == DamageSource.CACTUS) ||
+                (immuneToFire && (source.isFire() || source == DamageSource.IN_FIRE)) ||
+                (immuneToLightning && source == DamageSource.LIGHTNING_BOLT) ||
+                (immuneToExplosion && source.isExplosion())
+        ) {
+            respawnOnClient();
+            return false;
+        // Purpur end
         } else {
             // CraftBukkit start
             if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, amount)) {
@@ -510,6 +526,12 @@ public class ItemEntity extends Entity {
         com.google.common.base.Preconditions.checkArgument(!stack.isEmpty(), "Cannot drop air"); // CraftBukkit
         this.getEntityData().set(ItemEntity.DATA_ITEM, stack);
         this.getEntityData().markDirty(ItemEntity.DATA_ITEM); // CraftBukkit - SPIGOT-4591, must mark dirty
+        // Purpur start
+        if (level.purpurConfig.itemImmuneToCactus.contains(stack.getItem())) immuneToCactus = true;
+        if (level.purpurConfig.itemImmuneToExplosion.contains(stack.getItem())) immuneToExplosion = true;
+        if (level.purpurConfig.itemImmuneToFire.contains(stack.getItem())) immuneToFire = true;
+        if (level.purpurConfig.itemImmuneToLightning.contains(stack.getItem())) immuneToLightning = true;
+        // level end
     }
 
     @Override
@@ -600,4 +622,17 @@ public class ItemEntity extends Entity {
     public SoundSource getSoundSource() {
         return SoundSource.AMBIENT;
     }
+
+    // Purpur start
+    public void respawnOnClient() {
+        Packet<?> spawnPacket = new ClientboundAddEntityPacket(this);
+        Packet<?> metadataPacket = new net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket(getId(), getEntityData(), true);
+        for (net.minecraft.server.network.ServerPlayerConnection connection : this.tracker.serverEntity.trackedPlayers) {
+            if (!connection.getPlayer().purpurClient) {
+                connection.send(spawnPacket);
+                connection.send(metadataPacket);
+            }
+        }
+    }
+    // Purpur end
 }
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index dd00807ca9893f5919c6e76e30145e118ce343ea..18463b61d54170c1cce788e2175789f030da644c 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -111,6 +111,49 @@ public class PurpurWorldConfig {
         elytraDamagePerTridentBoost = getInt("gameplay-mechanics.elytra.damage-per-boost.trident", elytraDamagePerTridentBoost);
     }
 
+    public List<Item> itemImmuneToCactus = new ArrayList<>();
+    public List<Item> itemImmuneToExplosion = new ArrayList<>();
+    public List<Item> itemImmuneToFire = new ArrayList<>();
+    public List<Item> itemImmuneToLightning = new ArrayList<>();
+    private void itemSettings() {
+        itemImmuneToCactus.clear();
+        getList("gameplay-mechanics.item.immune.cactus", new ArrayList<>()).forEach(key -> {
+            if (key.toString().equals("*")) {
+                Registry.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToCactus.add(item));
+                return;
+            }
+            Item item = Registry.ITEM.get(new ResourceLocation(key.toString()));
+            if (item != Items.AIR) itemImmuneToCactus.add(item);
+        });
+        itemImmuneToExplosion.clear();
+        getList("gameplay-mechanics.item.immune.explosion", new ArrayList<>()).forEach(key -> {
+            if (key.toString().equals("*")) {
+                Registry.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToExplosion.add(item));
+                return;
+            }
+            Item item = Registry.ITEM.get(new ResourceLocation(key.toString()));
+            if (item != Items.AIR) itemImmuneToExplosion.add(item);
+        });
+        itemImmuneToFire.clear();
+        getList("gameplay-mechanics.item.immune.fire", new ArrayList<>()).forEach(key -> {
+            if (key.toString().equals("*")) {
+                Registry.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToFire.add(item));
+                return;
+            }
+            Item item = Registry.ITEM.get(new ResourceLocation(key.toString()));
+            if (item != Items.AIR) itemImmuneToFire.add(item);
+        });
+        itemImmuneToLightning.clear();
+        getList("gameplay-mechanics.item.immune.lightning", new ArrayList<>()).forEach(key -> {
+            if (key.toString().equals("*")) {
+                Registry.ITEM.stream().filter(item -> item != Items.AIR).forEach((item) -> itemImmuneToLightning.add(item));
+                return;
+            }
+            Item item = Registry.ITEM.get(new ResourceLocation(key.toString()));
+            if (item != Items.AIR) itemImmuneToLightning.add(item);
+        });
+    }
+
     public double minecartMaxSpeed = 0.4D;
     public boolean minecartPlaceAnywhere = false;
     public boolean minecartControllable = false;
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
index 342345eb04d00efb58392ccf209e3c51c1064173..5f5c37fb447a4c1790714158f4b41f15793002d1 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java
@@ -116,4 +116,46 @@ public class CraftItem extends CraftEntity implements Item {
     public EntityType getType() {
         return EntityType.DROPPED_ITEM;
     }
+
+    // Purpur start
+    @Override
+    public void setImmuneToCactus(boolean immuneToCactus) {
+        item.immuneToCactus = immuneToCactus;
+    }
+
+    @Override
+    public boolean isImmuneToCactus() {
+        return item.immuneToCactus;
+    }
+
+    @Override
+    public void setImmuneToExplosion(boolean immuneToExplosion) {
+        item.immuneToExplosion = immuneToExplosion;
+    }
+
+    @Override
+    public boolean isImmuneToExplosion() {
+        return item.immuneToExplosion;
+    }
+
+    @Override
+    public void setImmuneToFire(boolean immuneToFire) {
+        item.immuneToFire = immuneToFire;
+    }
+
+    @Override
+    public boolean isImmuneToFire() {
+        return item.immuneToFire;
+    }
+
+    @Override
+    public void setImmuneToLightning(boolean immuneToLightning) {
+        item.immuneToLightning = immuneToLightning;
+    }
+
+    @Override
+    public boolean isImmuneToLightning() {
+        return item.immuneToLightning;
+    }
+    // Purpur end
 }
