--- a/net/minecraft/world/entity/decoration/ItemFrame.java
+++ b/net/minecraft/world/entity/decoration/ItemFrame.java
@@ -2,6 +2,7 @@
 
 import com.mojang.logging.LogUtils;
 import javax.annotation.Nullable;
+import io.papermc.paper.event.player.PlayerItemFrameChangeEvent; // Paper - Add PlayerItemFrameChangeEvent
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
 import net.minecraft.core.component.DataComponents;
@@ -41,15 +42,18 @@
 import org.slf4j.Logger;
 
 public class ItemFrame extends HangingEntity {
+
     private static final Logger LOGGER = LogUtils.getLogger();
     public static final EntityDataAccessor<ItemStack> DATA_ITEM = SynchedEntityData.defineId(ItemFrame.class, EntityDataSerializers.ITEM_STACK);
     public static final EntityDataAccessor<Integer> DATA_ROTATION = SynchedEntityData.defineId(ItemFrame.class, EntityDataSerializers.INT);
     public static final int NUM_ROTATIONS = 8;
-    public float dropChance = 1.0F;
+    public float dropChance;
     public boolean fixed;
+    public @Nullable MapId cachedMapId; // Paper - Perf: Cache map ids on item frames
 
     public ItemFrame(EntityType<? extends ItemFrame> type, Level world) {
         super(type, world);
+        this.dropChance = 1.0F;
     }
 
     public ItemFrame(Level world, BlockPos pos, Direction facing) {
@@ -58,13 +62,14 @@
 
     public ItemFrame(EntityType<? extends ItemFrame> type, Level world, BlockPos pos, Direction facing) {
         super(type, world, pos);
+        this.dropChance = 1.0F;
         this.setDirection(facing);
     }
 
     @Override
     protected void defineSynchedData(SynchedEntityData.Builder builder) {
-        builder.define(DATA_ITEM, ItemStack.EMPTY);
-        builder.define(DATA_ROTATION, 0);
+        builder.define(ItemFrame.DATA_ITEM, ItemStack.EMPTY);
+        builder.define(ItemFrame.DATA_ROTATION, 0);
     }
 
     @Override
@@ -73,9 +78,9 @@
         this.direction = facing;
         if (facing.getAxis().isHorizontal()) {
             this.setXRot(0.0F);
-            this.setYRot((float)(this.direction.get2DDataValue() * 90));
+            this.setYRot((float) (this.direction.get2DDataValue() * 90));
         } else {
-            this.setXRot((float)(-90 * facing.getAxisDirection().getStep()));
+            this.setXRot((float) (-90 * facing.getAxisDirection().getStep()));
             this.setYRot(0.0F);
         }
 
@@ -87,43 +92,57 @@
     @Override
     protected void recalculateBoundingBox() {
         if (this.direction != null) {
-            double d = 0.46875;
-            double e = (double)this.pos.getX() + 0.5 - (double)this.direction.getStepX() * 0.46875;
-            double f = (double)this.pos.getY() + 0.5 - (double)this.direction.getStepY() * 0.46875;
-            double g = (double)this.pos.getZ() + 0.5 - (double)this.direction.getStepZ() * 0.46875;
-            this.setPosRaw(e, f, g);
-            double h = (double)this.getWidth();
-            double i = (double)this.getHeight();
-            double j = (double)this.getWidth();
-            Direction.Axis axis = this.direction.getAxis();
-            switch (axis) {
+            // CraftBukkit start code moved in to calculateBoundingBox
+            this.setBoundingBox(ItemFrame.calculateBoundingBox(this, this.pos, this.direction, this.getWidth(), this.getHeight()));
+            // CraftBukkit end
+        }
+    }
+
+    // CraftBukkit start - break out BB calc into own method
+    public static AABB calculateBoundingBox(@Nullable Entity entity, BlockPos blockPosition, Direction direction, int width, int height) {
+        {
+            double d0 = 0.46875D;
+            double d1 = (double) blockPosition.getX() + 0.5D - (double) direction.getStepX() * 0.46875D;
+            double d2 = (double) blockPosition.getY() + 0.5D - (double) direction.getStepY() * 0.46875D;
+            double d3 = (double) blockPosition.getZ() + 0.5D - (double) direction.getStepZ() * 0.46875D;
+
+            if (entity != null) {
+                entity.setPosRaw(d1, d2, d3);
+            }
+            double d4 = (double) width;
+            double d5 = (double) height;
+            double d6 = (double) width;
+            Direction.Axis enumdirection_enumaxis = direction.getAxis();
+
+            switch (enumdirection_enumaxis) {
                 case X:
-                    h = 1.0;
+                    d4 = 1.0D;
                     break;
                 case Y:
-                    i = 1.0;
+                    d5 = 1.0D;
                     break;
                 case Z:
-                    j = 1.0;
+                    d6 = 1.0D;
             }
 
-            h /= 32.0;
-            i /= 32.0;
-            j /= 32.0;
-            this.setBoundingBox(new AABB(e - h, f - i, g - j, e + h, f + i, g + j));
+            d4 /= 32.0D;
+            d5 /= 32.0D;
+            d6 /= 32.0D;
+            return new AABB(d1 - d4, d2 - d5, d3 - d6, d1 + d4, d2 + d5, d3 + d6);
         }
     }
+    // CraftBukkit end
 
     @Override
     public boolean survives() {
         if (this.fixed) {
             return true;
-        } else if (!this.level().noCollision(this)) {
+        } else if (!this.level().noCollision((Entity) this)) {
             return false;
         } else {
-            BlockState blockState = this.level().getBlockState(this.pos.relative(this.direction.getOpposite()));
-            return (blockState.isSolid() || this.direction.getAxis().isHorizontal() && DiodeBlock.isDiode(blockState))
-                && this.level().getEntities(this, this.getBoundingBox(), HANGING_ENTITY).isEmpty();
+            BlockState iblockdata = this.level().getBlockState(this.pos.relative(this.direction.getOpposite()));
+
+            return !iblockdata.isSolid() && (!this.direction.getAxis().isHorizontal() || !DiodeBlock.isDiode(iblockdata)) ? false : this.level().getEntities((Entity) this, this.getBoundingBox(), ItemFrame.HANGING_ENTITY).isEmpty();
         }
     }
 
@@ -132,13 +151,15 @@
         if (!this.fixed) {
             super.move(movementType, movement);
         }
+
     }
 
     @Override
-    public void push(double deltaX, double deltaY, double deltaZ) {
+    public void push(double deltaX, double deltaY, double deltaZ, @org.jetbrains.annotations.Nullable Entity pushingEntity) { // Paper - add push source entity param
         if (!this.fixed) {
-            super.push(deltaX, deltaY, deltaZ);
+            super.push(deltaX, deltaY, deltaZ, pushingEntity); // Paper - add push source entity param
         }
+
     }
 
     @Override
@@ -150,11 +171,23 @@
     @Override
     public boolean hurt(DamageSource source, float amount) {
         if (this.fixed) {
-            return (source.is(DamageTypeTags.BYPASSES_INVULNERABILITY) || source.isCreativePlayer()) && super.hurt(source, amount);
+            return !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY) && !source.isCreativePlayer() ? false : super.hurt(source, amount);
         } else if (this.isInvulnerableTo(source)) {
             return false;
         } else if (!source.is(DamageTypeTags.IS_EXPLOSION) && !this.getItem().isEmpty()) {
             if (!this.level().isClientSide) {
+                // CraftBukkit start - fire EntityDamageEvent
+                if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, amount, false) || this.isRemoved()) {
+                    return true;
+                }
+                // CraftBukkit end
+                // Paper start - Add PlayerItemFrameChangeEvent
+                if (source.getEntity() instanceof Player player) {
+                    var event = new PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), this.getItem().asBukkitCopy(), PlayerItemFrameChangeEvent.ItemFrameChangeAction.REMOVE);
+                    if (!event.callEvent()) return true; // return true here because you aren't cancelling the damage, just the change
+                    this.setItem(ItemStack.fromBukkitCopy(event.getItemStack()), false);
+                }
+                // Paper end - Add PlayerItemFrameChangeEvent
                 this.dropItem(source.getEntity(), false);
                 this.gameEvent(GameEvent.BLOCK_CHANGE, source.getEntity());
                 this.playSound(this.getRemoveItemSound(), 1.0F, 1.0F);
@@ -182,9 +215,10 @@
 
     @Override
     public boolean shouldRenderAtSqrDistance(double distance) {
-        double d = 16.0;
-        d *= 64.0 * getViewScale();
-        return distance < d * d;
+        double d1 = 16.0D;
+
+        d1 *= 64.0D * getViewScale();
+        return distance < d1 * d1;
     }
 
     @Override
@@ -209,53 +243,77 @@
 
     private void dropItem(@Nullable Entity entity, boolean alwaysDrop) {
         if (!this.fixed) {
-            ItemStack itemStack = this.getItem();
+            ItemStack itemstack = this.getItem();
+
             this.setItem(ItemStack.EMPTY);
             if (!this.level().getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
                 if (entity == null) {
-                    this.removeFramedMap(itemStack);
+                    this.removeFramedMap(itemstack);
                 }
+
             } else {
-                if (entity instanceof Player player && player.hasInfiniteMaterials()) {
-                    this.removeFramedMap(itemStack);
-                    return;
+                if (entity instanceof Player) {
+                    Player entityhuman = (Player) entity;
+
+                    if (entityhuman.hasInfiniteMaterials()) {
+                        this.removeFramedMap(itemstack);
+                        return;
+                    }
                 }
 
                 if (alwaysDrop) {
                     this.spawnAtLocation(this.getFrameItemStack());
                 }
 
-                if (!itemStack.isEmpty()) {
-                    itemStack = itemStack.copy();
-                    this.removeFramedMap(itemStack);
+                if (!itemstack.isEmpty()) {
+                    itemstack = itemstack.copy();
+                    this.removeFramedMap(itemstack);
                     if (this.random.nextFloat() < this.dropChance) {
-                        this.spawnAtLocation(itemStack);
+                        this.spawnAtLocation(itemstack);
                     }
                 }
+
             }
         }
     }
 
     private void removeFramedMap(ItemStack stack) {
-        MapId mapId = this.getFramedMapId();
-        if (mapId != null) {
-            MapItemSavedData mapItemSavedData = MapItem.getSavedData(mapId, this.level());
-            if (mapItemSavedData != null) {
-                mapItemSavedData.removedFromFrame(this.pos, this.getId());
-                mapItemSavedData.setDirty(true);
+        MapId mapid = this.getFramedMapIdForItem(stack); // Paper - fix MC-252817 (green map markers do not disappear)
+
+        if (mapid != null) {
+            MapItemSavedData worldmap = MapItem.getSavedData(mapid, this.level());
+
+            if (worldmap != null) {
+                worldmap.removedFromFrame(this.pos, this.getId());
+                worldmap.setDirty(true);
             }
         }
 
-        stack.setEntityRepresentation(null);
+        stack.setEntityRepresentation((Entity) null);
     }
 
     public ItemStack getItem() {
-        return this.getEntityData().get(DATA_ITEM);
+        return (ItemStack) this.getEntityData().get(ItemFrame.DATA_ITEM);
     }
 
+    // Paper start - Fix MC-123848 (spawn item frame drops above block)
+    @Nullable
+    @Override
+    public net.minecraft.world.entity.item.ItemEntity spawnAtLocation(ItemStack stack) {
+        return this.spawnAtLocation(stack, getDirection().equals(Direction.DOWN) ? -0.6F : 0.0F);
+    }
+    // Paper end
+
     @Nullable
     public MapId getFramedMapId() {
-        return this.getItem().get(DataComponents.MAP_ID);
+        // Paper start
+        return this.getFramedMapIdForItem(this.getItem());
+    }
+
+    @Nullable
+    public MapId getFramedMapIdForItem(ItemStack item) {
+        return (MapId) item.get(DataComponents.MAP_ID);
+        // Paper end
     }
 
     public boolean hasFramedMap() {
@@ -267,19 +325,26 @@
     }
 
     public void setItem(ItemStack value, boolean update) {
-        if (!value.isEmpty()) {
-            value = value.copyWithCount(1);
+        // CraftBukkit start
+        this.setItem(value, update, true);
+    }
+
+    public void setItem(ItemStack itemstack, boolean flag, boolean playSound) {
+        // CraftBukkit end
+        if (!itemstack.isEmpty()) {
+            itemstack = itemstack.copyWithCount(1);
         }
 
-        this.onItemChanged(value);
-        this.getEntityData().set(DATA_ITEM, value);
-        if (!value.isEmpty()) {
+        this.onItemChanged(itemstack);
+        this.getEntityData().set(ItemFrame.DATA_ITEM, itemstack);
+        if (!itemstack.isEmpty() && flag && playSound) { // CraftBukkit // Paper - only play sound when update flag is set
             this.playSound(this.getAddItemSound(), 1.0F, 1.0F);
         }
 
-        if (update && this.pos != null) {
+        if (flag && this.pos != null) {
             this.level().updateNeighbourForOutputSignal(this.pos, Blocks.AIR);
         }
+
     }
 
     public SoundEvent getAddItemSound() {
@@ -293,12 +358,14 @@
 
     @Override
     public void onSyncedDataUpdated(EntityDataAccessor<?> data) {
-        if (data.equals(DATA_ITEM)) {
+        if (data.equals(ItemFrame.DATA_ITEM)) {
             this.onItemChanged(this.getItem());
         }
+
     }
 
     private void onItemChanged(ItemStack stack) {
+        this.cachedMapId = stack.getComponents().get(net.minecraft.core.component.DataComponents.MAP_ID); // Paper - Perf: Cache map ids on item frames
         if (!stack.isEmpty() && stack.getFrame() != this) {
             stack.setEntityRepresentation(this);
         }
@@ -307,7 +374,7 @@
     }
 
     public int getRotation() {
-        return this.getEntityData().get(DATA_ROTATION);
+        return (Integer) this.getEntityData().get(ItemFrame.DATA_ROTATION);
     }
 
     public void setRotation(int value) {
@@ -315,10 +382,11 @@
     }
 
     private void setRotation(int value, boolean updateComparators) {
-        this.getEntityData().set(DATA_ROTATION, value % 8);
+        this.getEntityData().set(ItemFrame.DATA_ROTATION, value % 8);
         if (updateComparators && this.pos != null) {
             this.level().updateNeighbourForOutputSignal(this.pos, Blocks.AIR);
         }
+
     }
 
     @Override
@@ -326,11 +394,11 @@
         super.addAdditionalSaveData(nbt);
         if (!this.getItem().isEmpty()) {
             nbt.put("Item", this.getItem().save(this.registryAccess()));
-            nbt.putByte("ItemRotation", (byte)this.getRotation());
+            nbt.putByte("ItemRotation", (byte) this.getRotation());
             nbt.putFloat("ItemDropChance", this.dropChance);
         }
 
-        nbt.putByte("Facing", (byte)this.direction.get3DDataValue());
+        nbt.putByte("Facing", (byte) this.direction.get3DDataValue());
         nbt.putBoolean("Invisible", this.isInvisible());
         nbt.putBoolean("Fixed", this.fixed);
     }
@@ -338,21 +406,24 @@
     @Override
     public void readAdditionalSaveData(CompoundTag nbt) {
         super.readAdditionalSaveData(nbt);
-        ItemStack itemStack;
+        ItemStack itemstack;
+
         if (nbt.contains("Item", 10)) {
-            CompoundTag compoundTag = nbt.getCompound("Item");
-            itemStack = ItemStack.parse(this.registryAccess(), compoundTag).orElse(ItemStack.EMPTY);
+            CompoundTag nbttagcompound1 = nbt.getCompound("Item");
+
+            itemstack = (ItemStack) ItemStack.parse(this.registryAccess(), nbttagcompound1).orElse(ItemStack.EMPTY);
         } else {
-            itemStack = ItemStack.EMPTY;
+            itemstack = ItemStack.EMPTY;
         }
 
-        ItemStack itemStack3 = this.getItem();
-        if (!itemStack3.isEmpty() && !ItemStack.matches(itemStack, itemStack3)) {
-            this.removeFramedMap(itemStack3);
+        ItemStack itemstack1 = this.getItem();
+
+        if (!itemstack1.isEmpty() && !ItemStack.matches(itemstack, itemstack1)) {
+            this.removeFramedMap(itemstack1);
         }
 
-        this.setItem(itemStack, false);
-        if (!itemStack.isEmpty()) {
+        this.setItem(itemstack, false);
+        if (!itemstack.isEmpty()) {
             this.setRotation(nbt.getByte("ItemRotation"), false);
             if (nbt.contains("ItemDropChance", 99)) {
                 this.dropChance = nbt.getFloat("ItemDropChance");
@@ -366,26 +437,41 @@
 
     @Override
     public InteractionResult interact(Player player, InteractionHand hand) {
-        ItemStack itemStack = player.getItemInHand(hand);
-        boolean bl = !this.getItem().isEmpty();
-        boolean bl2 = !itemStack.isEmpty();
+        ItemStack itemstack = player.getItemInHand(hand);
+        boolean flag = !this.getItem().isEmpty();
+        boolean flag1 = !itemstack.isEmpty();
+
         if (this.fixed) {
             return InteractionResult.PASS;
         } else if (!this.level().isClientSide) {
-            if (!bl) {
-                if (bl2 && !this.isRemoved()) {
-                    if (itemStack.is(Items.FILLED_MAP)) {
-                        MapItemSavedData mapItemSavedData = MapItem.getSavedData(itemStack, this.level());
-                        if (mapItemSavedData != null && mapItemSavedData.isTrackedCountOverLimit(256)) {
+            if (!flag) {
+                if (flag1 && !this.isRemoved()) {
+                    if (itemstack.is(Items.FILLED_MAP)) {
+                        MapItemSavedData worldmap = MapItem.getSavedData(itemstack, this.level());
+
+                        if (worldmap != null && worldmap.isTrackedCountOverLimit(256)) {
                             return InteractionResult.FAIL;
                         }
                     }
 
-                    this.setItem(itemStack);
+                    // Paper start - Add PlayerItemFrameChangeEvent
+                    PlayerItemFrameChangeEvent event = new PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), itemstack.asBukkitCopy(), PlayerItemFrameChangeEvent.ItemFrameChangeAction.PLACE);
+                    if (!event.callEvent()) {
+                        return InteractionResult.FAIL;
+                    }
+                    this.setItem(ItemStack.fromBukkitCopy(event.getItemStack()));
+                    // Paper end - Add PlayerItemFrameChangeEvent
                     this.gameEvent(GameEvent.BLOCK_CHANGE, player);
-                    itemStack.consume(1, player);
+                    itemstack.consume(1, player);
                 }
             } else {
+                // Paper start - Add PlayerItemFrameChangeEvent
+                PlayerItemFrameChangeEvent event = new PlayerItemFrameChangeEvent((org.bukkit.entity.Player) player.getBukkitEntity(), (org.bukkit.entity.ItemFrame) this.getBukkitEntity(), this.getItem().asBukkitCopy(), PlayerItemFrameChangeEvent.ItemFrameChangeAction.ROTATE);
+                if (!event.callEvent()) {
+                    return InteractionResult.FAIL;
+                }
+                setItem(ItemStack.fromBukkitCopy(event.getItemStack()), false, false);
+                // Paper end - Add PlayerItemFrameChangeEvent
                 this.playSound(this.getRotateItemSound(), 1.0F, 1.0F);
                 this.setRotation(this.getRotation() + 1);
                 this.gameEvent(GameEvent.BLOCK_CHANGE, player);
@@ -393,7 +479,7 @@
 
             return InteractionResult.CONSUME;
         } else {
-            return !bl && !bl2 ? InteractionResult.PASS : InteractionResult.SUCCESS;
+            return !flag && !flag1 ? InteractionResult.PASS : InteractionResult.SUCCESS;
         }
     }
 
@@ -418,8 +504,9 @@
 
     @Override
     public ItemStack getPickResult() {
-        ItemStack itemStack = this.getItem();
-        return itemStack.isEmpty() ? this.getFrameItemStack() : itemStack.copy();
+        ItemStack itemstack = this.getItem();
+
+        return itemstack.isEmpty() ? this.getFrameItemStack() : itemstack.copy();
     }
 
     protected ItemStack getFrameItemStack() {
@@ -428,8 +515,9 @@
 
     @Override
     public float getVisualRotationYInDegrees() {
-        Direction direction = this.getDirection();
-        int i = direction.getAxis().isVertical() ? 90 * direction.getAxisDirection().getStep() : 0;
-        return (float)Mth.wrapDegrees(180 + direction.get2DDataValue() * 90 + this.getRotation() * 45 + i);
+        Direction enumdirection = this.getDirection();
+        int i = enumdirection.getAxis().isVertical() ? 90 * enumdirection.getAxisDirection().getStep() : 0;
+
+        return (float) Mth.wrapDegrees(180 + enumdirection.get2DDataValue() * 90 + this.getRotation() * 45 + i);
     }
 }
