minecraft-src/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
2025-07-04 03:15:13 +03:00

160 lines
5.6 KiB
Java

package net.minecraft.world.level.block.entity;
import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup.Provider;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.Container;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.JukeboxSong;
import net.minecraft.world.item.JukeboxSongPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.JukeboxBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.gameevent.GameEvent.Context;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.ticks.ContainerSingleItem.BlockContainerSingleItem;
public class JukeboxBlockEntity extends BlockEntity implements BlockContainerSingleItem {
public static final String SONG_ITEM_TAG_ID = "RecordItem";
public static final String TICKS_SINCE_SONG_STARTED_TAG_ID = "ticks_since_song_started";
private ItemStack item = ItemStack.EMPTY;
private final JukeboxSongPlayer jukeboxSongPlayer = new JukeboxSongPlayer(this::onSongChanged, this.getBlockPos());
public JukeboxBlockEntity(BlockPos pos, BlockState blockState) {
super(BlockEntityType.JUKEBOX, pos, blockState);
}
public JukeboxSongPlayer getSongPlayer() {
return this.jukeboxSongPlayer;
}
public void onSongChanged() {
this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
this.setChanged();
}
private void notifyItemChangedInJukebox(boolean hasRecord) {
if (this.level != null && this.level.getBlockState(this.getBlockPos()) == this.getBlockState()) {
this.level.setBlock(this.getBlockPos(), this.getBlockState().setValue(JukeboxBlock.HAS_RECORD, hasRecord), 2);
this.level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), Context.of(this.getBlockState()));
}
}
public void popOutTheItem() {
if (this.level != null && !this.level.isClientSide) {
BlockPos blockPos = 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);
}
}
}
public static void tick(Level level, BlockPos pos, BlockState state, JukeboxBlockEntity jukebox) {
jukebox.jukeboxSongPlayer.tick(level, state);
}
public int getComparatorOutput() {
return (Integer)JukeboxSong.fromStack(this.level.registryAccess(), this.item).map(Holder::value).map(JukeboxSong::comparatorOutput).orElse(0);
}
@Override
protected void loadAdditional(CompoundTag tag, Provider registries) {
super.loadAdditional(tag, registries);
if (tag.contains("RecordItem", 10)) {
this.item = (ItemStack)ItemStack.parse(registries, tag.getCompound("RecordItem")).orElse(ItemStack.EMPTY);
} else {
if (!this.item.isEmpty()) {
this.jukeboxSongPlayer.stop(this.level, this.getBlockState());
}
this.item = ItemStack.EMPTY;
}
if (tag.contains("ticks_since_song_started", 4)) {
JukeboxSong.fromStack(registries, this.item)
.ifPresent(holder -> this.jukeboxSongPlayer.setSongWithoutPlaying(holder, tag.getLong("ticks_since_song_started")));
}
}
@Override
protected void saveAdditional(CompoundTag tag, Provider registries) {
super.saveAdditional(tag, registries);
if (!this.getTheItem().isEmpty()) {
tag.put("RecordItem", this.getTheItem().save(registries));
}
if (this.jukeboxSongPlayer.getSong() != null) {
tag.putLong("ticks_since_song_started", this.jukeboxSongPlayer.getTicksSinceSongStarted());
}
}
@Override
public ItemStack getTheItem() {
return this.item;
}
@Override
public ItemStack splitTheItem(int amount) {
ItemStack itemStack = this.item;
this.setTheItem(ItemStack.EMPTY);
return itemStack;
}
@Override
public void setTheItem(ItemStack item) {
this.item = item;
boolean bl = !this.item.isEmpty();
Optional<Holder<JukeboxSong>> optional = JukeboxSong.fromStack(this.level.registryAccess(), this.item);
this.notifyItemChangedInJukebox(bl);
if (bl && optional.isPresent()) {
this.jukeboxSongPlayer.play(this.level, (Holder<JukeboxSong>)optional.get());
} else {
this.jukeboxSongPlayer.stop(this.level, this.getBlockState());
}
}
@Override
public int getMaxStackSize() {
return 1;
}
@Override
public BlockEntity getContainerBlockEntity() {
return this;
}
@Override
public boolean canPlaceItem(int slot, ItemStack stack) {
return stack.has(DataComponents.JUKEBOX_PLAYABLE) && this.getItem(slot).isEmpty();
}
@Override
public boolean canTakeItem(Container target, int slot, ItemStack stack) {
return target.hasAnyMatching(ItemStack::isEmpty);
}
@VisibleForTesting
public void setSongItemWithoutPlaying(ItemStack stack) {
this.item = stack;
JukeboxSong.fromStack(this.level.registryAccess(), stack).ifPresent(holder -> this.jukeboxSongPlayer.setSongWithoutPlaying(holder, 0L));
this.level.updateNeighborsAt(this.getBlockPos(), this.getBlockState().getBlock());
this.setChanged();
}
@VisibleForTesting
public void tryForcePlaySong() {
JukeboxSong.fromStack(this.level.registryAccess(), this.getTheItem()).ifPresent(holder -> this.jukeboxSongPlayer.play(this.level, holder));
}
}