--- a/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
@@ -3,6 +3,7 @@
 import com.google.common.annotations.VisibleForTesting;
 import javax.annotation.Nullable;
 import net.minecraft.core.BlockPos;
+import net.minecraft.core.Holder;
 import net.minecraft.core.HolderLookup;
 import net.minecraft.core.particles.ParticleTypes;
 import net.minecraft.nbt.CompoundTag;
@@ -12,6 +13,7 @@
 import net.minecraft.world.Container;
 import net.minecraft.world.entity.Entity;
 import net.minecraft.world.entity.item.ItemEntity;
+import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.item.Item;
 import net.minecraft.world.item.ItemStack;
 import net.minecraft.world.item.RecordItem;
@@ -22,23 +24,69 @@
 import net.minecraft.world.phys.Vec3;
 import net.minecraft.world.ticks.ContainerSingleItem;
 
+// CraftBukkit start
+import java.util.Collections;
+import java.util.List;
+import org.bukkit.Location;
+import org.bukkit.craftbukkit.entity.CraftHumanEntity;
+import org.bukkit.entity.HumanEntity;
+// CraftBukkit end
+
 public class JukeboxBlockEntity extends BlockEntity implements Clearable, ContainerSingleItem.BlockContainerSingleItem {
+
     private static final int SONG_END_PADDING = 20;
-    private ItemStack item = ItemStack.EMPTY;
+    private ItemStack item;
     private int ticksSinceLastEvent;
     public long tickCount;
     public long recordStartedTick;
     public boolean isPlaying;
+    // CraftBukkit start - add fields and methods
+    public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
+    private int maxStack = MAX_STACK;
+    public boolean opened;
+
+    @Override
+    public List<ItemStack> getContents() {
+        return Collections.singletonList(this.item);
+    }
+
+    @Override
+    public void onOpen(CraftHumanEntity who) {
+        this.transaction.add(who);
+    }
+
+    @Override
+    public void onClose(CraftHumanEntity who) {
+        this.transaction.remove(who);
+    }
+
+    @Override
+    public List<HumanEntity> getViewers() {
+        return this.transaction;
+    }
+
+    @Override
+    public void setMaxStackSize(int size) {
+        this.maxStack = size;
+    }
+
+    @Override
+    public Location getLocation() {
+        if (this.level == null) return null;
+        return new org.bukkit.Location(this.level.getWorld(), this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ());
+    }
+    // CraftBukkit end
 
     public JukeboxBlockEntity(BlockPos pos, BlockState state) {
         super(BlockEntityType.JUKEBOX, pos, state);
+        this.item = ItemStack.EMPTY;
     }
 
     @Override
     protected void loadAdditional(CompoundTag nbt, HolderLookup.Provider registryLookup) {
         super.loadAdditional(nbt, registryLookup);
         if (nbt.contains("RecordItem", 10)) {
-            this.item = ItemStack.parse(registryLookup, nbt.getCompound("RecordItem")).orElse(ItemStack.EMPTY);
+            this.item = (ItemStack) ItemStack.parse(registryLookup, nbt.getCompound("RecordItem")).orElse(ItemStack.EMPTY);
         } else {
             this.item = ItemStack.EMPTY;
         }
@@ -66,9 +114,10 @@
 
     private void setHasRecordBlockState(@Nullable Entity entity, boolean hasRecord) {
         if (this.level.getBlockState(this.getBlockPos()) == this.getBlockState()) {
-            this.level.setBlock(this.getBlockPos(), this.getBlockState().setValue(JukeboxBlock.HAS_RECORD, Boolean.valueOf(hasRecord)), 2);
-            this.level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(entity, this.getBlockState()));
+            this.level.setBlock(this.getBlockPos(), (BlockState) this.getBlockState().setValue(JukeboxBlock.HAS_RECORD, hasRecord), 2);
+            this.level.gameEvent((Holder) GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(entity, this.getBlockState()));
         }
+
     }
 
     @VisibleForTesting
@@ -76,35 +125,41 @@
         this.recordStartedTick = this.tickCount;
         this.isPlaying = true;
         this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
-        this.level.levelEvent(null, 1010, this.getBlockPos(), Item.getId(this.getTheItem().getItem()));
+        this.level.levelEvent((Player) null, 1010, this.getBlockPos(), Item.getId(this.getTheItem().getItem()));
         this.setChanged();
     }
 
     private void stopPlaying() {
         this.isPlaying = false;
-        this.level.gameEvent(GameEvent.JUKEBOX_STOP_PLAY, this.getBlockPos(), GameEvent.Context.of(this.getBlockState()));
+        this.level.gameEvent((Holder) GameEvent.JUKEBOX_STOP_PLAY, this.getBlockPos(), GameEvent.Context.of(this.getBlockState()));
         this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
         this.level.levelEvent(1011, this.getBlockPos(), 0);
         this.setChanged();
     }
 
     private void tick(Level world, BlockPos pos, BlockState state) {
-        this.ticksSinceLastEvent++;
-        if (this.isRecordPlaying() && this.getTheItem().getItem() instanceof RecordItem recordItem) {
-            if (this.shouldRecordStopPlaying(recordItem)) {
-                this.stopPlaying();
-            } else if (this.shouldSendJukeboxPlayingEvent()) {
-                this.ticksSinceLastEvent = 0;
-                world.gameEvent(GameEvent.JUKEBOX_PLAY, pos, GameEvent.Context.of(state));
-                this.spawnMusicParticles(world, pos);
+        ++this.ticksSinceLastEvent;
+        if (this.isRecordPlaying()) {
+            Item item = this.getTheItem().getItem();
+
+            if (item instanceof RecordItem) {
+                RecordItem itemrecord = (RecordItem) item;
+
+                if (this.shouldRecordStopPlaying(itemrecord)) {
+                    this.stopPlaying();
+                } else if (this.shouldSendJukeboxPlayingEvent()) {
+                    this.ticksSinceLastEvent = 0;
+                    world.gameEvent((Holder) GameEvent.JUKEBOX_PLAY, pos, GameEvent.Context.of(state));
+                    this.spawnMusicParticles(world, pos);
+                }
             }
         }
 
-        this.tickCount++;
+        ++this.tickCount;
     }
 
     private boolean shouldRecordStopPlaying(RecordItem musicDisc) {
-        return this.tickCount >= this.recordStartedTick + (long)musicDisc.getLengthInTicks() + 20L;
+        return this.tickCount >= this.recordStartedTick + (long) musicDisc.getLengthInTicks() + 20L;
     }
 
     private boolean shouldSendJukeboxPlayingEvent() {
@@ -118,30 +173,32 @@
 
     @Override
     public ItemStack splitTheItem(int count) {
-        ItemStack itemStack = this.item;
+        ItemStack itemstack = this.item;
+
         this.item = ItemStack.EMPTY;
-        if (!itemStack.isEmpty()) {
-            this.setHasRecordBlockState(null, false);
+        if (!itemstack.isEmpty()) {
+            this.setHasRecordBlockState((Entity) null, false);
             this.stopPlaying();
         }
 
-        return itemStack;
+        return itemstack;
     }
 
     @Override
     public void setTheItem(ItemStack stack) {
         if (stack.is(ItemTags.MUSIC_DISCS) && this.level != null) {
             this.item = stack;
-            this.setHasRecordBlockState(null, true);
+            this.setHasRecordBlockState((Entity) null, true);
             this.startPlaying();
         } else if (stack.isEmpty()) {
             this.splitTheItem(1);
         }
+
     }
 
     @Override
     public int getMaxStackSize() {
-        return 1;
+        return this.maxStack; // CraftBukkit
     }
 
     @Override
@@ -160,24 +217,28 @@
     }
 
     private void spawnMusicParticles(Level world, BlockPos pos) {
-        if (world instanceof ServerLevel serverLevel) {
-            Vec3 vec3 = Vec3.atBottomCenterOf(pos).add(0.0, 1.2F, 0.0);
-            float f = (float)world.getRandom().nextInt(4) / 24.0F;
-            serverLevel.sendParticles(ParticleTypes.NOTE, vec3.x(), vec3.y(), vec3.z(), 0, (double)f, 0.0, 0.0, 1.0);
+        if (world instanceof ServerLevel worldserver) {
+            Vec3 vec3d = Vec3.atBottomCenterOf(pos).add(0.0D, 1.2000000476837158D, 0.0D);
+            float f = (float) world.getRandom().nextInt(4) / 24.0F;
+
+            worldserver.sendParticles(ParticleTypes.NOTE, vec3d.x(), vec3d.y(), vec3d.z(), 0, (double) f, 0.0D, 0.0D, 1.0D);
         }
+
     }
 
     public void popOutRecord() {
         if (this.level != null && !this.level.isClientSide) {
-            BlockPos blockPos = this.getBlockPos();
-            ItemStack itemStack = this.getTheItem();
-            if (!itemStack.isEmpty()) {
+            BlockPos blockposition = this.getBlockPos();
+            ItemStack itemstack = this.getTheItem();
+
+            if (!itemstack.isEmpty()) {
                 this.removeTheItem();
-                Vec3 vec3 = Vec3.atLowerCornerWithOffset(blockPos, 0.5, 1.01, 0.5).offsetRandom(this.level.random, 0.7F);
-                ItemStack itemStack2 = itemStack.copy();
-                ItemEntity itemEntity = new ItemEntity(this.level, vec3.x(), vec3.y(), vec3.z(), itemStack2);
-                itemEntity.setDefaultPickUpDelay();
-                this.level.addFreshEntity(itemEntity);
+                Vec3 vec3d = Vec3.atLowerCornerWithOffset(blockposition, 0.5D, 1.01D, 0.5D).offsetRandom(this.level.random, 0.7F);
+                ItemStack itemstack1 = itemstack.copy();
+                ItemEntity entityitem = new ItemEntity(this.level, vec3d.x(), vec3d.y(), vec3d.z(), itemstack1);
+
+                entityitem.setDefaultPickUpDelay();
+                this.level.addFreshEntity(entityitem);
             }
         }
     }
@@ -189,7 +250,11 @@
     @VisibleForTesting
     public void setRecordWithoutPlaying(ItemStack stack) {
         this.item = stack;
-        this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
+        // CraftBukkit start - add null check for level
+        if (this.level != null) {
+            this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
+        }
+        // CraftBukkit end
         this.setChanged();
     }
 }
