package net.minecraft.world.level.block.entity; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.mojang.authlib.GameProfile; import com.mojang.authlib.yggdrasil.ProfileResult; import java.time.Duration; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.BooleanSupplier; import javax.annotation.Nullable; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; import net.minecraft.core.component.DataComponentGetter; import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponents; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.Services; import net.minecraft.util.StringUtil; import net.minecraft.world.item.component.ResolvableProfile; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.SkullBlock; import net.minecraft.world.level.block.state.BlockState; public class SkullBlockEntity extends BlockEntity { private static final String TAG_PROFILE = "profile"; private static final String TAG_NOTE_BLOCK_SOUND = "note_block_sound"; private static final String TAG_CUSTOM_NAME = "custom_name"; @Nullable private static Executor mainThreadExecutor; @Nullable private static LoadingCache>> profileCacheByName; @Nullable private static LoadingCache>> profileCacheById; public static final Executor CHECKED_MAIN_THREAD_EXECUTOR = p_296964_ -> { Executor executor = mainThreadExecutor; if (executor != null) { executor.execute(p_296964_); } }; @Nullable private ResolvableProfile owner; @Nullable private ResourceLocation noteBlockSound; private int animationTickCount; private boolean isAnimating; @Nullable private Component customName; public SkullBlockEntity(BlockPos p_155731_, BlockState p_155732_) { super(BlockEntityType.SKULL, p_155731_, p_155732_); } public static void setup(final Services p_222886_, Executor p_222887_) { mainThreadExecutor = p_222887_; final BooleanSupplier booleansupplier = () -> profileCacheById == null; profileCacheByName = CacheBuilder.newBuilder() .expireAfterAccess(Duration.ofMinutes(10L)) .maximumSize(256L) .build(new CacheLoader>>() { public CompletableFuture> load(String p_312380_) { return SkullBlockEntity.fetchProfileByName(p_312380_, p_222886_); } }); profileCacheById = CacheBuilder.newBuilder() .expireAfterAccess(Duration.ofMinutes(10L)) .maximumSize(256L) .build(new CacheLoader>>() { public CompletableFuture> load(UUID p_330530_) { return SkullBlockEntity.fetchProfileById(p_330530_, p_222886_, booleansupplier); } }); } static CompletableFuture> fetchProfileByName(String p_333451_, Services p_332839_) { return p_332839_.profileCache() .getAsync(p_333451_) .thenCompose( p_327322_ -> { LoadingCache>> loadingcache = profileCacheById; return loadingcache != null && !p_327322_.isEmpty() ? loadingcache.getUnchecked(p_327322_.get().getId()).thenApply(p_327317_ -> p_327317_.or(() -> p_327322_)) : CompletableFuture.completedFuture(Optional.empty()); } ); } static CompletableFuture> fetchProfileById(UUID p_332548_, Services p_336268_, BooleanSupplier p_335205_) { return CompletableFuture.supplyAsync(() -> { if (p_335205_.getAsBoolean()) { return Optional.empty(); } else { ProfileResult profileresult = p_336268_.sessionService().fetchProfile(p_332548_, true); return Optional.ofNullable(profileresult).map(ProfileResult::profile); } }, Util.backgroundExecutor().forName("fetchProfile")); } public static void clear() { mainThreadExecutor = null; profileCacheByName = null; profileCacheById = null; } @Override protected void saveAdditional(CompoundTag p_187518_, HolderLookup.Provider p_329143_) { super.saveAdditional(p_187518_, p_329143_); p_187518_.storeNullable("profile", ResolvableProfile.CODEC, this.owner); p_187518_.storeNullable("note_block_sound", ResourceLocation.CODEC, this.noteBlockSound); p_187518_.storeNullable("custom_name", ComponentSerialization.CODEC, p_329143_.createSerializationContext(NbtOps.INSTANCE), this.customName); } @Override protected void loadAdditional(CompoundTag p_335831_, HolderLookup.Provider p_329643_) { super.loadAdditional(p_335831_, p_329643_); this.setOwner(p_335831_.read("profile", ResolvableProfile.CODEC).orElse(null)); this.noteBlockSound = p_335831_.read("note_block_sound", ResourceLocation.CODEC).orElse(null); this.customName = parseCustomNameSafe(p_335831_.get("custom_name"), p_329643_); } public static void animation(Level p_261710_, BlockPos p_262153_, BlockState p_262021_, SkullBlockEntity p_261594_) { if (p_262021_.hasProperty(SkullBlock.POWERED) && p_262021_.getValue(SkullBlock.POWERED)) { p_261594_.isAnimating = true; p_261594_.animationTickCount++; } else { p_261594_.isAnimating = false; } } public float getAnimation(float p_262053_) { return this.isAnimating ? this.animationTickCount + p_262053_ : this.animationTickCount; } @Nullable public ResolvableProfile getOwnerProfile() { return this.owner; } @Nullable public ResourceLocation getNoteBlockSound() { return this.noteBlockSound; } public ClientboundBlockEntityDataPacket getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); } @Override public CompoundTag getUpdateTag(HolderLookup.Provider p_335540_) { return this.saveCustomOnly(p_335540_); } public void setOwner(@Nullable ResolvableProfile p_328553_) { synchronized (this) { this.owner = p_328553_; } this.updateOwnerProfile(); } private void updateOwnerProfile() { if (this.owner != null && !this.owner.isResolved()) { this.owner.resolve().thenAcceptAsync(p_327314_ -> { this.owner = p_327314_; this.setChanged(); }, CHECKED_MAIN_THREAD_EXECUTOR); } else { this.setChanged(); } } public static CompletableFuture> fetchGameProfile(String p_298654_) { LoadingCache>> loadingcache = profileCacheByName; return loadingcache != null && StringUtil.isValidPlayerName(p_298654_) ? loadingcache.getUnchecked(p_298654_) : CompletableFuture.completedFuture(Optional.empty()); } public static CompletableFuture> fetchGameProfile(UUID p_331248_) { LoadingCache>> loadingcache = profileCacheById; return loadingcache != null ? loadingcache.getUnchecked(p_331248_) : CompletableFuture.completedFuture(Optional.empty()); } @Override protected void applyImplicitComponents(DataComponentGetter p_393162_) { super.applyImplicitComponents(p_393162_); this.setOwner(p_393162_.get(DataComponents.PROFILE)); this.noteBlockSound = p_393162_.get(DataComponents.NOTE_BLOCK_SOUND); this.customName = p_393162_.get(DataComponents.CUSTOM_NAME); } @Override protected void collectImplicitComponents(DataComponentMap.Builder p_335245_) { super.collectImplicitComponents(p_335245_); p_335245_.set(DataComponents.PROFILE, this.owner); p_335245_.set(DataComponents.NOTE_BLOCK_SOUND, this.noteBlockSound); p_335245_.set(DataComponents.CUSTOM_NAME, this.customName); } @Override public void removeComponentsFromTag(CompoundTag p_332333_) { super.removeComponentsFromTag(p_332333_); p_332333_.remove("profile"); p_332333_.remove("note_block_sound"); p_332333_.remove("custom_name"); } }