From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Wed, 30 Sep 2020 14:32:46 -0700
Subject: [PATCH] Persistent TileEntity Lore and DisplayName

Makes it so that when a TileEntity is placed in the world and then broken,
the dropped ItemStack retains any original custom display name/lore.

diff --git a/src/main/java/net/minecraft/world/item/BlockItem.java b/src/main/java/net/minecraft/world/item/BlockItem.java
index 8e6df16568c0dab482e10ad1b38920d77f6e684f..6c261b9ddee80139140bd1f091fcdacc3a70c8ef 100644
--- a/src/main/java/net/minecraft/world/item/BlockItem.java
+++ b/src/main/java/net/minecraft/world/item/BlockItem.java
@@ -137,7 +137,24 @@ public class BlockItem extends Item {
     }
 
     protected boolean updateCustomBlockEntityTag(BlockPos pos, Level world, @Nullable Player player, ItemStack stack, BlockState state) {
-        return BlockItem.updateCustomBlockEntityTag(world, player, pos, stack);
+        // Purpur start
+        boolean handled = updateCustomBlockEntityTag(world, player, pos, stack);
+        if (world.purpurConfig.persistentTileEntityDisplayNames && stack.hasTag()) {
+            CompoundTag display = stack.getTagElement("display");
+            if (display != null) {
+                BlockEntity blockEntity = world.getBlockEntity(pos);
+                if (blockEntity != null) {
+                    if (display.contains("Name", 8)) {
+                        blockEntity.setPersistentDisplayName(display.getString("Name"));
+                    }
+                    if (display.contains("Lore", 9)) {
+                        blockEntity.setPersistentLore(display.getList("Lore", 8));
+                    }
+                }
+            }
+        }
+        return handled;
+        // Purpur end
     }
 
     @Nullable
diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java
index 1a5605b11170dfe1bd37165de886bad0f2e38ecd..94a69b7a1443231cf807fb792478c35334f73e62 100644
--- a/src/main/java/net/minecraft/world/level/block/Block.java
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
@@ -20,6 +20,9 @@ import net.minecraft.core.IdMapper;
 import net.minecraft.core.NonNullList;
 import net.minecraft.core.Registry;
 import net.minecraft.core.Vec3i;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.nbt.StringTag;
 import net.minecraft.network.chat.Component;
 import net.minecraft.network.chat.MutableComponent;
 import net.minecraft.network.chat.TranslatableComponent;
@@ -28,6 +31,7 @@ import net.minecraft.stats.Stats;
 import net.minecraft.tags.BlockTags;
 import net.minecraft.tags.Tag;
 import net.minecraft.util.Mth;
+import net.minecraft.world.Nameable;
 import net.minecraft.world.damagesource.DamageSource;
 import net.minecraft.world.entity.Entity;
 import net.minecraft.world.entity.EntityType;
@@ -325,7 +329,7 @@ public class Block extends BlockBehaviour implements ItemLike {
     public static void dropResources(BlockState state, LevelAccessor world, BlockPos pos, @Nullable BlockEntity blockEntity) {
         if (world instanceof ServerLevel) {
             Block.getDrops(state, (ServerLevel) world, pos, blockEntity).forEach((itemstack) -> {
-                Block.popResource((Level) ((ServerLevel) world), pos, itemstack);
+                Block.popResource((Level) ((ServerLevel) world), pos, applyDisplayNameAndLoreFromTile(itemstack, blockEntity)); // Purpur
             });
             state.spawnAfterBreak((ServerLevel) world, pos, ItemStack.EMPTY);
         }
@@ -335,13 +339,53 @@ public class Block extends BlockBehaviour implements ItemLike {
     public static void dropResources(BlockState state, Level world, BlockPos pos, @Nullable BlockEntity blockEntity, Entity entity, ItemStack stack) {
         if (world instanceof ServerLevel) {
             Block.getDrops(state, (ServerLevel) world, pos, blockEntity, entity, stack).forEach((itemstack1) -> {
-                Block.popResource(world, pos, itemstack1);
+                Block.popResource(world, pos, applyDisplayNameAndLoreFromTile(itemstack1, blockEntity)); // Purpur
             });
             state.spawnAfterBreak((ServerLevel) world, pos, stack);
         }
 
     }
 
+    // Purpur start
+    private static ItemStack applyDisplayNameAndLoreFromTile(ItemStack stack, BlockEntity blockEntity) {
+        if (stack.getItem() instanceof BlockItem) {
+            if (blockEntity != null && blockEntity.getLevel() instanceof ServerLevel && blockEntity.getLevel().purpurConfig.persistentTileEntityDisplayNames) {
+                String name = blockEntity.getPersistentDisplayName();
+                ListTag lore = blockEntity.getPersistentLore();
+                if (blockEntity instanceof Nameable) {
+                    Nameable namedTile = (Nameable) blockEntity;
+                    if (namedTile.hasCustomName()) {
+                        name = Component.Serializer.toJson(namedTile.getCustomName());
+                    }
+                }
+
+                if (name != null || lore != null) {
+                    CompoundTag display = stack.getTagElement("display");
+                    if (display == null) {
+                        display = new CompoundTag();
+                    }
+
+                    if (name != null) {
+                        display.put("Name", StringTag.valueOf(name));
+                    }
+                    if (lore != null) {
+                        display.put("Lore", lore);
+                    }
+
+                    CompoundTag tag = stack.getTag();
+                    if (tag == null) {
+                        tag = new CompoundTag();
+                    }
+                    tag.put("display", display);
+
+                    stack.setTag(tag);
+                }
+            }
+        }
+        return stack;
+    }
+    // Purpur end
+
     public static void popResource(Level world, BlockPos pos, ItemStack stack) {
         float f = EntityType.ITEM.getHeight() / 2.0F;
         // Paper start - don't convert potentially massive numbers to floats
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
index c3a07ccccd5cc38552363c82398f432c8d624288..132c9e6a643995d9fde535a78d9edc9ee97e3a76 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
@@ -5,6 +5,8 @@ import net.minecraft.CrashReportCategory;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Registry;
 import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.nbt.StringTag;
 import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.world.level.Level;
@@ -87,10 +89,26 @@ public abstract class BlockEntity implements net.minecraft.server.KeyedObject {
         if (persistentDataTag instanceof CompoundTag) {
             this.persistentDataContainer.putAll((CompoundTag) persistentDataTag);
         }
+        // Purpur start
+        if (nbt.contains("Purpur.persistentDisplayName")) {
+            this.persistentDisplayName = nbt.getString("Purpur.persistentDisplayName");
+        }
+        if (nbt.contains("Purpur.persistentLore")) {
+            this.persistentLore = nbt.getList("Purpur.persistentLore", 8);
+        }
+        // Purpur end
     }
     // CraftBukkit end
 
     public CompoundTag save(CompoundTag nbt) {
+        // Purpur start
+        if (this.persistentDisplayName != null) {
+            nbt.put("Purpur.persistentDisplayName", StringTag.valueOf(this.persistentDisplayName));
+        }
+        if (this.persistentLore != null) {
+            nbt.put("Purpur.persistentLore", this.persistentLore);
+        }
+        // Purpur end
         return this.saveMetadata(nbt);
     }
 
@@ -243,4 +261,25 @@ public abstract class BlockEntity implements net.minecraft.server.KeyedObject {
         return null;
     }
     // CraftBukkit end
+
+    // Purpur start
+    private String persistentDisplayName = null;
+    private ListTag persistentLore = null;
+
+    public void setPersistentDisplayName(String json) {
+        this.persistentDisplayName = json;
+    }
+
+    public void setPersistentLore(ListTag lore) {
+        this.persistentLore = lore;
+    }
+
+    public String getPersistentDisplayName() {
+        return this.persistentDisplayName;
+    }
+
+    public ListTag getPersistentLore() {
+        return this.persistentLore;
+    }
+    // Purpur end
 }
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index 1484f492df6366dec71aa1ced77ea6f8037675ca..e1aab2eee7b3c3148f48417126bee94e91968033 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -331,6 +331,7 @@ public class PurpurWorldConfig {
     public boolean disableDropsOnCrammingDeath = false;
     public boolean entitiesCanUsePortals = true;
     public boolean milkCuresBadOmen = true;
+    public boolean persistentTileEntityDisplayNames = false;
     public double tridentLoyaltyVoidReturnHeight = 0.0D;
     public double voidDamageHeight = -64.0D;
     public double voidDamageDealt = 4.0D;
@@ -341,6 +342,7 @@ public class PurpurWorldConfig {
         disableDropsOnCrammingDeath = getBoolean("gameplay-mechanics.disable-drops-on-cramming-death", disableDropsOnCrammingDeath);
         entitiesCanUsePortals = getBoolean("gameplay-mechanics.entities-can-use-portals", entitiesCanUsePortals);
         milkCuresBadOmen = getBoolean("gameplay-mechanics.milk-cures-bad-omen", milkCuresBadOmen);
+        persistentTileEntityDisplayNames = getBoolean("gameplay-mechanics.persistent-tileentity-display-names-and-lore", persistentTileEntityDisplayNames);
         tridentLoyaltyVoidReturnHeight = getDouble("gameplay-mechanics.trident-loyalty-void-return-height", tridentLoyaltyVoidReturnHeight);
         voidDamageHeight = getDouble("gameplay-mechanics.void-damage-height", voidDamageHeight);
         voidDamageDealt = getDouble("gameplay-mechanics.void-damage-dealt", voidDamageDealt);
