From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:53:30 +0300 Subject: [PATCH] lithium: sleeping_block_entity This patch is based on the following mixins: * "net/caffeinemc/mods/lithium/mixin/world/block_entity_ticking/sleeping/*.java" * "net/caffeinemc/mods/lithium/mixin/block/hopper/*.java" * "net/caffeinemc/mods/lithium/mixin/util/block_entity_retrieval/LevelMixin.java" * "net/caffeinemc/mods/lithium/mixin/util/inventory_change_listening/*.java" * "net/caffeinemc/mods/lithium/mixin/util/inventory_comparator_tracking/*.java" * "net/caffeinemc/mods/lithium/mixin/util/item_component_and_count_tracking/*.java" By: 2No2Name <2No2Name@web.de> As part of: Lithium (https://github.com/CaffeineMC/lithium) Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) diff --git a/net/minecraft/core/component/PatchedDataComponentMap.java b/net/minecraft/core/component/PatchedDataComponentMap.java index 766b6080160d87742ef4d8caa73b3b8fa52d5589..541d22fdc559717c7fda692c23abae581ee05a7f 100644 --- a/net/minecraft/core/component/PatchedDataComponentMap.java +++ b/net/minecraft/core/component/PatchedDataComponentMap.java @@ -14,7 +14,7 @@ import java.util.Map.Entry; import java.util.stream.Collectors; import javax.annotation.Nullable; -public final class PatchedDataComponentMap implements DataComponentMap { +public final class PatchedDataComponentMap implements DataComponentMap, net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher { // DivineMC - lithium: sleeping_block_entity private final DataComponentMap prototype; private Reference2ObjectMap, Optional> patch; private boolean copyOnWrite; @@ -140,6 +140,7 @@ public final class PatchedDataComponentMap implements DataComponentMap { } private void ensureMapOwnership() { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.subscriber != null) this.subscriber.lithium$notify((PatchedDataComponentMap) (Object) this, 0); // DivineMC - lithium: sleeping_block_entity if (this.copyOnWrite) { this.patch = new Reference2ObjectArrayMap<>(this.patch); this.copyOnWrite = false; @@ -243,4 +244,22 @@ public final class PatchedDataComponentMap implements DataComponentMap { public String toString() { return "{" + this.stream().map(TypedDataComponent::toString).collect(Collectors.joining(", ")) + "}"; } + + // DivineMC start - lithium: sleeping_block_entity + private net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber; + + @Override + public void lithium$subscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { + if (subscriberData != 0) { + throw new UnsupportedOperationException("ComponentMapImpl does not support subscriber data"); + } + this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.combine(this.subscriber, 0, subscriber, 0); + } + + @Override + public int lithium$unsubscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber) { + this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.without(this.subscriber, subscriber); + return 0; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/server/commands/data/EntityDataAccessor.java b/net/minecraft/server/commands/data/EntityDataAccessor.java index 2b8d394dc30d459127289a1afeee0780003f4c79..2fd2712550a9b8ac79974f113ebcea5dab392bc5 100644 --- a/net/minecraft/server/commands/data/EntityDataAccessor.java +++ b/net/minecraft/server/commands/data/EntityDataAccessor.java @@ -55,6 +55,7 @@ public class EntityDataAccessor implements DataAccessor { try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(this.entity.problemPath(), LOGGER)) { this.entity.load(TagValueInput.create(scopedCollector, this.entity.registryAccess(), other)); this.entity.setUUID(uuid); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.entity instanceof net.minecraft.world.entity.item.ItemEntity itemEntity) itemEntity.levelCallback.onMove(); // DivineMC - lithium: sleeping_block_entity } } } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index 14790f65b00ede0c063c567f524674270ca58b5c..f0ad8c869e22c7c10e59e88effd4b0ebffb3f62c 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -2520,6 +2520,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe for (TickingBlockEntity tickingBlockEntity : this.blockEntityTickers) { BlockPos pos = tickingBlockEntity.getPos(); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && pos == null) pos = BlockPos.ZERO; // DivineMC - lithium: sleeping_block_entity csvOutput.writeRow(pos.getX(), pos.getY(), pos.getZ(), tickingBlockEntity.getType()); } } diff --git a/net/minecraft/world/Container.java b/net/minecraft/world/Container.java index 8e6f097b4d17aaaf8eccc16e11ce2bd01ad63322..ded99b157865f5bcfd64b3082c628a71d3747507 100644 --- a/net/minecraft/world/Container.java +++ b/net/minecraft/world/Container.java @@ -13,7 +13,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; -public interface Container extends Clearable, Iterable { +public interface Container extends Clearable, Iterable, net.caffeinemc.mods.lithium.api.inventory.LithiumCooldownReceivingInventory, net.caffeinemc.mods.lithium.api.inventory.LithiumTransferConditionInventory { // DivineMC - lithium: sleeping_block_entity float DEFAULT_DISTANCE_BUFFER = 4.0F; int getContainerSize(); diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java index 5410bb67893ad01524d302b5d6c3952f3ffb7ee5..f6a599a4841d9598b6e1bf766e57d0505a363606 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -5099,6 +5099,18 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name this.setBoundingBox(this.makeBoundingBox()); } // Paper end - Block invalid positions and bounding box + // DivineMC start - lithium: sleeping_block_entity + if (!org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) return; + if (this instanceof ItemEntity) { + long sectionKey = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionKey(this); + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionItemEntityMovementTracker tracker = net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionItemEntityMovementTracker.itemEntityMovementTrackerMap.get(new net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.ChunkSectionIdentifier(sectionKey, level.getMinecraftWorld().uuid)); + if (tracker != null) tracker.notifyAllListeners(level.getGameTime()); + } else if (this instanceof net.minecraft.world.entity.vehicle.ContainerEntity) { + long sectionKey = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionKey(this); + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker tracker = net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker.containerEntityMovementTrackerMap.get(new net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.ChunkSectionIdentifier(sectionKey, level.getMinecraftWorld().uuid)); + if (tracker != null) tracker.notifyAllListeners(level.getGameTime()); + } + // DivineMC end - lithium: sleeping_block_entity } public void checkDespawn() { diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java index 7525184fd5a801e39fbcba2afbcc968862a1cfbb..c074cd9b83015970028ac19c7afafb94d95713c9 100644 --- a/net/minecraft/world/entity/item/ItemEntity.java +++ b/net/minecraft/world/entity/item/ItemEntity.java @@ -35,7 +35,7 @@ import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.Vec3; -public class ItemEntity extends Entity implements TraceableEntity { +public class ItemEntity extends Entity implements TraceableEntity, net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher, net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.CountChangeSubscriber { // DivineMC - lithium: sleeping_block_entity private static final EntityDataAccessor DATA_ITEM = SynchedEntityData.defineId(ItemEntity.class, EntityDataSerializers.ITEM_STACK); private static final float FLOAT_HEIGHT = 0.1F; public static final float EYE_HEIGHT = 0.2125F; @@ -542,6 +542,25 @@ public class ItemEntity extends Entity implements TraceableEntity { } public void setItem(ItemStack stack) { + // DivineMC start - lithium: sleeping_block_entity + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.subscriber != null) { + ItemStack oldStack = this.getItem(); + if (oldStack != stack) { + if (!oldStack.isEmpty()) { + oldStack.lithium$unsubscribe(this); + } + + if (!stack.isEmpty()) { + stack.lithium$subscribe(this, this.subscriberData); + this.subscriber.lithium$notify((ItemEntity) (Object) this, this.subscriberData); + } else { + this.subscriber.lithium$forceUnsubscribe((ItemEntity) (Object) this, this.subscriberData); + this.subscriber = null; + this.subscriberData = 0; + } + } + } + // DivineMC end - lithium: sleeping_block_entity this.getEntityData().set(DATA_ITEM, stack); this.despawnRate = this.level().paperConfig().entities.spawning.altItemDespawnRate.enabled ? this.level().paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(stack.getItem(), this.level().spigotConfig.itemDespawnRate) : this.level().spigotConfig.itemDespawnRate; // Paper - Alternative item-despawn-rate // Purpur start - Item entity immunities @@ -623,4 +642,75 @@ public class ItemEntity extends Entity implements TraceableEntity { public SlotAccess getSlot(int slot) { return slot == 0 ? SlotAccess.of(this::getItem, this::setItem) : super.getSlot(slot); } + + // DivineMC start - lithium: sleeping_block_entity + private net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber; + private int subscriberData; + + private void startTrackingChanges() { + ItemStack stack = this.getItem(); + if (!stack.isEmpty()) { + stack.lithium$subscribe(this, 0); + } + } + + @Override + public void lithium$subscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { + if (this.subscriber == null) { + this.startTrackingChanges(); + } + this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.combine(this.subscriber, this.subscriberData, subscriber, subscriberData); + if (this.subscriber instanceof net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.Multi) { + this.subscriberData = 0; + } else { + this.subscriberData = subscriberData; + } + } + + @Override + public int lithium$unsubscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber) { + int retval = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.dataOf(this.subscriber, subscriber, this.subscriberData); + this.subscriberData = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.dataWithout(this.subscriber, subscriber, this.subscriberData); + this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.without(this.subscriber, subscriber); + + if (this.subscriber == null) { + ItemStack stack = this.getItem(); + if (!stack.isEmpty()) { + stack.lithium$unsubscribe(this); + } + } + return retval; + } + + @Override + public void lithium$notify(ItemStack publisher, int subscriberData) { + if (publisher != this.getItem()) { + throw new IllegalStateException("Received notification from an unexpected publisher"); + } + + if (this.subscriber != null) { + this.subscriber.lithium$notify(this, this.subscriberData); + } + } + + @Override + public void lithium$forceUnsubscribe(ItemStack publisher, int subscriberData) { + if (this.subscriber != null) { + this.subscriber.lithium$forceUnsubscribe(this, this.subscriberData); + this.subscriber = null; + this.subscriberData = 0; + } + } + + @Override + public void lithium$notifyCount(ItemStack publisher, int subscriberData, int newCount) { + if (publisher != this.getItem()) { + throw new IllegalStateException("Received notification from an unexpected publisher"); + } + + if (this.subscriber instanceof net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.CountChangeSubscriber countChangeSubscriber) { + countChangeSubscriber.lithium$notifyCount(this, this.subscriberData, newCount); + } + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java index 0f9bfcd1eab023c1772e9fafcda85d110904bd1c..a54dbf14af2abcf0307c1c43fb82cfd6ab13bfd1 100644 --- a/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java +++ b/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java @@ -21,7 +21,7 @@ import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.phys.Vec3; -public abstract class AbstractMinecartContainer extends AbstractMinecart implements ContainerEntity { +public abstract class AbstractMinecartContainer extends AbstractMinecart implements ContainerEntity, net.caffeinemc.mods.lithium.api.inventory.LithiumInventory { // DivineMC - lithium: sleeping_block_entity private NonNullList itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 @Nullable public ResourceKey lootTable; @@ -218,4 +218,16 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme return this.getBukkitEntity().getLocation(); } // CraftBukkit end + + // DivineMC start - lithium: sleeping_block_entity + @Override + public net.minecraft.core.NonNullList getInventoryLithium() { + return itemStacks; + } + + @Override + public void setInventoryLithium(net.minecraft.core.NonNullList inventory) { + itemStacks = inventory; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java index c3021c0a8c588acf5ae8c9231e75bda984863267..0f3f9efdce86c3fb50247245eb5a3e1c1a14a794 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -901,6 +901,12 @@ public abstract class AbstractContainerMenu { } else { float f = 0.0F; + // DivineMC start - lithium: sleeping_block_entity + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && container instanceof net.caffeinemc.mods.lithium.api.inventory.LithiumInventory inventory) { + return net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(inventory).getSignalStrength(container); + } + // DivineMC end - lithium: sleeping_block_entity + for (int i = 0; i < container.getContainerSize(); i++) { ItemStack item = container.getItem(i); if (!item.isEmpty()) { diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java index 52a8a818a636e35b486b8aaf482cd68849a0bbcd..7706bf3cdd8017eb0591a4002489835b97bb4918 100644 --- a/net/minecraft/world/item/ItemStack.java +++ b/net/minecraft/world/item/ItemStack.java @@ -96,7 +96,7 @@ import org.apache.commons.lang3.function.TriConsumer; import org.apache.commons.lang3.mutable.MutableBoolean; import org.slf4j.Logger; -public final class ItemStack implements DataComponentHolder { +public final class ItemStack implements DataComponentHolder, net.caffeinemc.mods.lithium.common.util.change_tracking.ChangePublisher, net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber { // DivineMC - lithium: sleeping_block_entity private static final List OP_NBT_WARNING = List.of( Component.translatable("item.op_warning.line1").withStyle(ChatFormatting.RED, ChatFormatting.BOLD), Component.translatable("item.op_warning.line2").withStyle(ChatFormatting.RED), @@ -983,6 +983,7 @@ public final class ItemStack implements DataComponentHolder { @Nullable public T set(DataComponentType component, @Nullable T value) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && component == DataComponents.ENCHANTMENTS && this.subscriber instanceof net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.EnchantmentSubscriber enchantmentSubscriber) enchantmentSubscriber.lithium$notifyAfterEnchantmentChange(this, this.subscriberData); // DivineMC - lithium: sleeping_block_entity return this.components.set(component, value); } @@ -1336,6 +1337,23 @@ public final class ItemStack implements DataComponentHolder { } public void setCount(int count) { + // DivineMC start - lithium: sleeping_block_entity + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && count != this.count) { + if (this.subscriber instanceof net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.CountChangeSubscriber countChangeSubscriber) { + countChangeSubscriber.lithium$notifyCount(this, this.subscriberData, count); + } + + if (count == 0) { + this.components.lithium$unsubscribe(this); + + if (this.subscriber != null) { + this.subscriber.lithium$forceUnsubscribe(this, this.subscriberData); + this.subscriber = null; + this.subscriberData = 0; + } + } + } + // DivineMC end - lithium: sleeping_block_entity this.count = count; } @@ -1391,4 +1409,90 @@ public final class ItemStack implements DataComponentHolder { public boolean canDestroyBlock(BlockState state, Level level, BlockPos pos, Player player) { return this.getItem().canDestroyBlock(this, state, level, pos, player); } + + // DivineMC start - lithium: sleeping_block_entity + private net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber; + private int subscriberData; + + @Override + public void lithium$subscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { + if (this.isEmpty()) { + throw new IllegalStateException("Cannot subscribe to an empty ItemStack!"); + } + + if (this.subscriber == null) { + this.startTrackingChanges(); + } + this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.combine(this.subscriber, this.subscriberData, subscriber, subscriberData); + if (this.subscriber instanceof net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.Multi) { + this.subscriberData = 0; + } else { + this.subscriberData = subscriberData; + } + } + + @Override + public int lithium$unsubscribe(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber) { + if (this.isEmpty()) { + throw new IllegalStateException("Cannot unsubscribe from an empty ItemStack!"); + } + + int retval = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.dataOf(this.subscriber, subscriber, this.subscriberData); + this.subscriberData = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.dataWithout(this.subscriber, subscriber, this.subscriberData); + this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.without(this.subscriber, subscriber); + + if (this.subscriber == null) { + this.components.lithium$unsubscribe(this); + } + return retval; + } + + @Override + public void lithium$unsubscribeWithData(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { + if (this.isEmpty()) { + throw new IllegalStateException("Cannot unsubscribe from an empty ItemStack!"); + } + + this.subscriberData = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.dataWithout(this.subscriber, subscriber, this.subscriberData, subscriberData, true); + this.subscriber = net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.without(this.subscriber, subscriber, subscriberData, true); + + if (this.subscriber == null) { + this.components.lithium$unsubscribe(this); + } + } + + @Override + public boolean lithium$isSubscribedWithData(net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { + if (this.isEmpty()) { + throw new IllegalStateException("Cannot be subscribed to an empty ItemStack!"); + } + + return net.caffeinemc.mods.lithium.common.util.change_tracking.ChangeSubscriber.containsSubscriber(this.subscriber, this.subscriberData, subscriber, subscriberData); + } + + @Override + public void lithium$forceUnsubscribe(PatchedDataComponentMap publisher, int subscriberData) { + if (publisher != this.components) { + throw new IllegalStateException("Invalid publisher, expected " + this.components + " but got " + publisher); + } + this.subscriber.lithium$forceUnsubscribe(this, this.subscriberData); + this.subscriber = null; + this.subscriberData = 0; + } + + private void startTrackingChanges() { + this.components.lithium$subscribe(this, 0); + } + + @Override + public void lithium$notify(PatchedDataComponentMap publisher, int subscriberData) { + if (publisher != this.components) { + throw new IllegalStateException("Invalid publisher, expected " + this.components + " but got " + publisher); + } + + if (this.subscriber != null) { + this.subscriber.lithium$notify(this, this.subscriberData); + } + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java index 4ec89234607f58ee32d0ea2ef056c8098e137041..e05c873814b6212ea61a4914e8a34cb9ac17822b 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java @@ -1477,7 +1477,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl // Spigot end if (tickingBlockEntity.isRemoved()) { this.blockEntityTickers.markAsRemoved(this.tileTickPosition); // DivineMC - optimize block entity removals - Fix MC-117075 - } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { + } else if (runsNormally && this.shouldTickBlockPosFilterNull(tickingBlockEntity.getPos())) { // DivineMC - lithium: sleeping_block_entity tickingBlockEntity.tick(); // DivineMC start - Parallel world ticking ++tickedEntities; @@ -2197,4 +2197,25 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl return getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END; } // Purpur end - Add allow water in end world option + + // DivineMC start - lithium: sleeping_block_entity + public BlockEntity lithium$getLoadedExistingBlockEntity(BlockPos pos) { + if (!this.isOutsideBuildHeight(pos)) { + if (this.isClientSide || Thread.currentThread() == this.thread) { + ChunkAccess chunk = this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()), ChunkStatus.FULL, false); + if (chunk != null) { + return chunk.getBlockEntity(pos); + } + } + } + return null; + } + + private boolean shouldTickBlockPosFilterNull(BlockPos pos) { + if (pos == null) { + return false; + } + return shouldTickBlocksAt(pos); + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/ComposterBlock.java b/net/minecraft/world/level/block/ComposterBlock.java index 90ef0ea828303ccfa568c6acb3975c48f628261a..da18bce7cb9e99d207476471b511643e7eb609b9 100644 --- a/net/minecraft/world/level/block/ComposterBlock.java +++ b/net/minecraft/world/level/block/ComposterBlock.java @@ -440,7 +440,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { } } - public static class EmptyContainer extends SimpleContainer implements WorldlyContainer { + public static class EmptyContainer extends SimpleContainer implements WorldlyContainer, net.caffeinemc.mods.lithium.common.hopper.BlockStateOnlyInventory { // DivineMC - lithium: sleeping_block_entity public EmptyContainer(LevelAccessor levelAccessor, BlockPos blockPos) { // CraftBukkit super(0); this.bukkitOwner = new org.bukkit.craftbukkit.inventory.CraftBlockInventoryHolder(levelAccessor, blockPos, this); // CraftBukkit @@ -462,7 +462,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { } } - public static class InputContainer extends SimpleContainer implements WorldlyContainer { + public static class InputContainer extends SimpleContainer implements WorldlyContainer, net.caffeinemc.mods.lithium.common.hopper.BlockStateOnlyInventory { // DivineMC - lithium: sleeping_block_entity private final BlockState state; private final LevelAccessor level; private final BlockPos pos; @@ -508,12 +508,13 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { } // Paper end - Add CompostItemEvent and EntityCompostItemEvent this.level.levelEvent(LevelEvent.COMPOSTER_FILL, this.pos, blockState != this.state ? 1 : 0); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.changed = false; // DivineMC - lithium: sleeping_block_entity this.removeItemNoUpdate(0); } } } - public static class OutputContainer extends SimpleContainer implements WorldlyContainer { + public static class OutputContainer extends SimpleContainer implements WorldlyContainer, net.caffeinemc.mods.lithium.common.hopper.BlockStateOnlyInventory { // DivineMC - lithium: sleeping_block_entity private final BlockState state; private final LevelAccessor level; private final BlockPos pos; diff --git a/net/minecraft/world/level/block/DiodeBlock.java b/net/minecraft/world/level/block/DiodeBlock.java index 1158bdc1993a1c8f907070d6190c5a7d02d4bdb6..0fccdb394438bc35cdd38741a9c91bb022741c3d 100644 --- a/net/minecraft/world/level/block/DiodeBlock.java +++ b/net/minecraft/world/level/block/DiodeBlock.java @@ -173,6 +173,7 @@ public abstract class DiodeBlock extends HorizontalDirectionalBlock { @Override protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { this.updateNeighborsInFront(level, pos, state); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this instanceof ComparatorBlock && !oldState.is(Blocks.COMPARATOR)) net.caffeinemc.mods.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracking.notifyNearbyBlockEntitiesAboutNewComparator(level, pos); // DivineMC - lithium: sleeping_block_entity } @Override diff --git a/net/minecraft/world/level/block/HopperBlock.java b/net/minecraft/world/level/block/HopperBlock.java index 3140269761935201882173e568004488147a4110..288215bb11c0b63db7c354d12995ac5756382404 100644 --- a/net/minecraft/world/level/block/HopperBlock.java +++ b/net/minecraft/world/level/block/HopperBlock.java @@ -38,7 +38,7 @@ import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -public class HopperBlock extends BaseEntityBlock { +public class HopperBlock extends BaseEntityBlock implements net.caffeinemc.mods.lithium.common.block.entity.ShapeUpdateHandlingBlockBehaviour { // DivineMC - lithium: sleeping_block_entity public static final MapCodec CODEC = simpleCodec(HopperBlock::new); public static final EnumProperty FACING = BlockStateProperties.FACING_HOPPER; public static final BooleanProperty ENABLED = BlockStateProperties.ENABLED; @@ -101,6 +101,16 @@ public class HopperBlock extends BaseEntityBlock { protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { if (!oldState.is(state.getBlock())) { this.checkPoweredState(level, pos, state); + // DivineMC start - lithium: sleeping_block_entity + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && level.getBlockState(pos) != state) { + for (Direction direction : UPDATE_SHAPE_ORDER) { + BlockEntity hopper = level.lithium$getLoadedExistingBlockEntity(pos.relative(direction)); + if (hopper instanceof net.caffeinemc.mods.lithium.common.hopper.UpdateReceiver updateReceiver) { + updateReceiver.lithium$invalidateCacheOnNeighborUpdate(direction == Direction.DOWN); + } + } + } + // DivineMC end - lithium: sleeping_block_entity } } @@ -115,6 +125,7 @@ public class HopperBlock extends BaseEntityBlock { @Override protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && level.lithium$getLoadedExistingBlockEntity(pos) instanceof net.caffeinemc.mods.lithium.common.hopper.UpdateReceiver updateReceiver) updateReceiver.lithium$invalidateCacheOnUndirectedNeighborUpdate(); // DivineMC - lithium: sleeping_block_entity this.checkPoweredState(level, pos, state); } @@ -168,4 +179,24 @@ public class HopperBlock extends BaseEntityBlock { protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) { return false; } + + // DivineMC start - lithium: sleeping_block_entity + @Override + public void lithium$handleShapeUpdate(net.minecraft.world.level.LevelReader levelReader, BlockState myBlockState, BlockPos myPos, BlockPos posFrom, BlockState newState) { + if (newState.getBlock() instanceof net.minecraft.world.WorldlyContainerHolder) { + this.updateHopper(levelReader, myBlockState, myPos, posFrom); + } + } + + private void updateHopper(net.minecraft.world.level.LevelReader world, BlockState myBlockState, BlockPos myPos, BlockPos posFrom) { + Direction facing = myBlockState.getValue(HopperBlock.FACING); + boolean above = posFrom.getY() == myPos.getY() + 1; + if (above || posFrom.getX() == myPos.getX() + facing.getStepX() && posFrom.getY() == myPos.getY() + facing.getStepY() && posFrom.getZ() == myPos.getZ() + facing.getStepZ()) { + BlockEntity hopper = ((net.minecraft.world.level.Level) world).lithium$getLoadedExistingBlockEntity(myPos); + if (hopper instanceof net.caffeinemc.mods.lithium.common.hopper.UpdateReceiver updateReceiver) { + updateReceiver.lithium$invalidateCacheOnNeighborUpdate(above); + } + } + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java index 3dfd539f4453d9c47c99af36e7ba64400fc89a5c..9d6928f886b7fc164553144ff985e0e93d8635d1 100644 --- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java @@ -40,7 +40,7 @@ import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.Vec3; -public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, RecipeCraftingHolder, StackedContentsCompatible { +public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, RecipeCraftingHolder, StackedContentsCompatible, net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity, net.caffeinemc.mods.lithium.common.block.entity.SetChangedHandlingBlockEntity, net.caffeinemc.mods.lithium.api.inventory.LithiumInventory { // DivineMC - lithium: sleeping_block_entity protected static final int SLOT_INPUT = 0; protected static final int SLOT_FUEL = 1; protected static final int SLOT_RESULT = 2; @@ -165,6 +165,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit this.recipesUsed.clear(); this.recipesUsed.putAll(input.read("RecipesUsed", RECIPES_USED_CODEC).orElse(Map.of())); this.cookSpeedMultiplier = input.getDoubleOr("Paper.CookSpeedMultiplier", 1); // Paper - cook speed multiplier API + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.isSleeping() && this.level != null) this.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity } @Override @@ -286,6 +287,12 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit setChanged(level, pos, state); } + // DivineMC start - lithium: sleeping_block_entity + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) { + furnace.checkSleep(state); + } + // DivineMC end - lithium: sleeping_block_entity + if (usedLavaFromUnderneath) furnace.items.set(1, ItemStack.EMPTY); // Purpur - Furnace uses lava from underneath } @@ -545,4 +552,53 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit this.getRecipesToAwardAndPopExperience(serverLevel, Vec3.atCenterOf(pos)); } } + + // DivineMC start - lithium: sleeping_block_entity + private net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + @Override + public net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { + return tickWrapper; + } + + @Override + public void lithium$setTickWrapper(net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper) { + this.tickWrapper = tickWrapper; + this.lithium$setSleepingTicker(null); + } + + @Override + public TickingBlockEntity lithium$getSleepingTicker() { + return sleepingTicker; + } + + @Override + public void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + + private void checkSleep(BlockState state) { + if (!this.isLit() && this.cookingTimer == 0 && (state.is(Blocks.FURNACE) || state.is(Blocks.BLAST_FURNACE) || state.is(Blocks.SMOKER)) && this.level != null) { + this.lithium$startSleeping(); + } + } + + @Override + public void lithium$handleSetChanged() { + if (this.isSleeping() && this.level != null && !this.level.isClientSide) { + this.wakeUpNow(); + } + } + + @Override + public net.minecraft.core.NonNullList getInventoryLithium() { + return items; + } + + @Override + public void setInventoryLithium(net.minecraft.core.NonNullList inventory) { + items = inventory; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java index 04f6b3c328377091734a111f5a219379e32b5640..cec2996063356d0b1ac52d479b0e2ce3c5fa5078 100644 --- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java @@ -23,7 +23,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; -public class BarrelBlockEntity extends RandomizableContainerBlockEntity { +public class BarrelBlockEntity extends RandomizableContainerBlockEntity implements net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, net.caffeinemc.mods.lithium.api.inventory.LithiumInventory { // DivineMC - lithium: sleeping_block_entity // CraftBukkit start - add fields and methods public java.util.List transaction = new java.util.ArrayList<>(); private int maxStack = MAX_STACK; @@ -140,6 +140,7 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { @Override protected void setItems(NonNullList items) { this.items = items; + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.lithium$emitStackListReplaced(); // DivineMC - lithium: sleeping_block_entity } @Override @@ -198,4 +199,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { double d2 = this.worldPosition.getZ() + 0.5 + unitVec3i.getZ() / 2.0; this.level.playSound(null, d, d1, d2, sound, SoundSource.BLOCKS, 0.5F, this.level.random.nextFloat() * 0.1F + 0.9F); } + + // DivineMC start - lithium: sleeping_block_entity + @Override + public net.minecraft.core.NonNullList getInventoryLithium() { + return items; + } + + @Override + public void setInventoryLithium(net.minecraft.core.NonNullList inventory) { + items = inventory; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java index 7c2acea8af6a3110d782b9b3afeac0915ac127da..ccad8ed53e846ee0838420a74c8dc4b3e033e0d2 100644 --- a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java @@ -24,7 +24,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; -public abstract class BaseContainerBlockEntity extends BlockEntity implements Container, MenuProvider, Nameable { +public abstract class BaseContainerBlockEntity extends BlockEntity implements Container, MenuProvider, Nameable, net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeEmitter { // DivineMC - lithium: sleeping_block_entity public LockCode lockKey = LockCode.NO_LOCK; @Nullable public Component name; @@ -38,6 +38,7 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co super.loadAdditional(input); this.lockKey = LockCode.fromTag(input); this.name = parseCustomNameSafe(input, "CustomName"); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker inventoryChangeTracker) inventoryChangeTracker.lithium$emitStackListReplaced(); // DivineMC - lithium: sleeping_block_entity } @Override @@ -210,4 +211,98 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co return org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.worldPosition, this.level); } // CraftBukkit end + + // DivineMC start - lithium: sleeping_block_entity + it.unimi.dsi.fastutil.objects.ReferenceArraySet inventoryChangeListeners = null; + it.unimi.dsi.fastutil.objects.ReferenceArraySet inventoryHandlingTypeListeners = null; + + @Override + public void lithium$emitContentModified() { + it.unimi.dsi.fastutil.objects.ReferenceArraySet inventoryChangeListeners = this.inventoryChangeListeners; + if (inventoryChangeListeners != null) { + for (net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener inventoryChangeListener : inventoryChangeListeners) { + inventoryChangeListener.lithium$handleInventoryContentModified(this); + } + inventoryChangeListeners.clear(); + } + } + + @Override + public void lithium$emitStackListReplaced() { + it.unimi.dsi.fastutil.objects.ReferenceArraySet listeners = this.inventoryHandlingTypeListeners; + if (listeners != null && !listeners.isEmpty()) { + for (net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener inventoryChangeListener : listeners) { + inventoryChangeListener.handleStackListReplaced(this); + } + listeners.clear(); + } + + if (this instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener listener) { + listener.handleStackListReplaced(this); + } + + this.invalidateChangeListening(); + } + + @Override + public void lithium$emitRemoved() { + it.unimi.dsi.fastutil.objects.ReferenceArraySet listeners = this.inventoryHandlingTypeListeners; + if (listeners != null && !listeners.isEmpty()) { + for (net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener listener : listeners) { + listener.lithium$handleInventoryRemoved(this); + } + listeners.clear(); + } + + if (this instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener listener) { + listener.lithium$handleInventoryRemoved(this); + } + + this.invalidateChangeListening(); + } + + private void invalidateChangeListening() { + if (this.inventoryChangeListeners != null) { + this.inventoryChangeListeners.clear(); + } + + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList lithiumStackList = this instanceof net.caffeinemc.mods.lithium.api.inventory.LithiumInventory ? net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackListOrNull((net.caffeinemc.mods.lithium.api.inventory.LithiumInventory) this) : null; + if (lithiumStackList != null && this instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker inventoryChangeTracker) { + lithiumStackList.removeInventoryModificationCallback(inventoryChangeTracker); + } + } + + @Override + public void lithium$emitFirstComparatorAdded() { + it.unimi.dsi.fastutil.objects.ReferenceArraySet inventoryChangeListeners = this.inventoryChangeListeners; + if (inventoryChangeListeners != null && !inventoryChangeListeners.isEmpty()) { + inventoryChangeListeners.removeIf(inventoryChangeListener -> inventoryChangeListener.lithium$handleComparatorAdded(this)); + } + } + + @Override + public void lithium$forwardContentChangeOnce(net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener inventoryChangeListener, net.caffeinemc.mods.lithium.common.hopper.LithiumStackList stackList, net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker thisTracker) { + if (this.inventoryChangeListeners == null) { + this.inventoryChangeListeners = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(1); + } + stackList.setInventoryModificationCallback(thisTracker); + this.inventoryChangeListeners.add(inventoryChangeListener); + + } + + @Override + public void lithium$forwardMajorInventoryChanges(net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener inventoryChangeListener) { + if (this.inventoryHandlingTypeListeners == null) { + this.inventoryHandlingTypeListeners = new it.unimi.dsi.fastutil.objects.ReferenceArraySet<>(1); + } + this.inventoryHandlingTypeListeners.add(inventoryChangeListener); + } + + @Override + public void lithium$stopForwardingMajorInventoryChanges(net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener inventoryChangeListener) { + if (this.inventoryHandlingTypeListeners != null) { + this.inventoryHandlingTypeListeners.remove(inventoryChangeListener); + } + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java index 9a9208d3716c2ed7fbcc5ea8518d4b7d3cb2296a..6a2f56e9925185813b84a70fdc8709801d0e2839 100644 --- a/net/minecraft/world/level/block/entity/BlockEntity.java +++ b/net/minecraft/world/level/block/entity/BlockEntity.java @@ -36,7 +36,7 @@ import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import org.slf4j.Logger; -public abstract class BlockEntity implements DebugValueSource { +public abstract class BlockEntity implements DebugValueSource, net.caffeinemc.mods.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracker, net.caffeinemc.mods.lithium.common.block.entity.SetBlockStateHandlingBlockEntity, net.caffeinemc.mods.lithium.common.block.entity.SetChangedHandlingBlockEntity { // DivineMC - lithium: sleeping_block_entity static boolean ignoreBlockEntityUpdates; // Paper - Perf: Optimize Hoppers // CraftBukkit start - data containers private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); @@ -58,6 +58,7 @@ public abstract class BlockEntity implements DebugValueSource { this.validateBlockState(blockState); this.blockState = blockState; this.persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(DATA_TYPE_REGISTRY); // Paper - always init + this.hasComparators = UNKNOWN; // DivineMC - lithium: sleeping_block_entity } private void validateBlockState(BlockState state) { @@ -241,6 +242,7 @@ public abstract class BlockEntity implements DebugValueSource { if (this.level != null) { if (ignoreBlockEntityUpdates) return; // Paper - Perf: Optimize Hoppers setChanged(this.level, this.worldPosition, this.blockState); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) lithium$handleSetChanged(); // DivineMC - lithium: sleeping_block_entity } } @@ -273,7 +275,9 @@ public abstract class BlockEntity implements DebugValueSource { } public void setRemoved() { + this.hasComparators = UNKNOWN; // DivineMC - lithium: sleeping_block_entity this.remove = true; + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.level != null && !this.level.isClientSide() && this instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker inventoryChangeTracker) inventoryChangeTracker.lithium$emitRemoved(); // DivineMC - lithium: sleeping_block_entity } public void clearRemoved() { @@ -313,6 +317,7 @@ public abstract class BlockEntity implements DebugValueSource { public void setBlockState(BlockState blockState) { this.validateBlockState(blockState); this.blockState = blockState; + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.lithium$handleSetBlockState(); // DivineMC - lithium: sleeping_block_entity } protected void applyImplicitComponents(DataComponentGetter componentGetter) { @@ -430,4 +435,32 @@ public abstract class BlockEntity implements DebugValueSource { return this.persistentLore; } // Purpur end - Persistent BlockEntity Lore and DisplayName + + // DivineMC start - lithium: sleeping_block_entity + private static final byte UNKNOWN = (byte) -1; + private static final byte COMPARATOR_PRESENT = (byte) 1; + private static final byte COMPARATOR_ABSENT = (byte) 0; + + byte hasComparators; + + @Override + public void lithium$onComparatorAdded(net.minecraft.core.Direction direction, int offset) { + byte hasComparators = this.hasComparators; + if (direction.getAxis() != net.minecraft.core.Direction.Axis.Y && hasComparators != COMPARATOR_PRESENT && offset >= 1 && offset <= 2) { + this.hasComparators = COMPARATOR_PRESENT; + + if (this instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker inventoryChangeTracker) { + inventoryChangeTracker.lithium$emitFirstComparatorAdded(); + } + } + } + + @Override + public boolean lithium$hasAnyComparatorNearby() { + if (this.hasComparators == UNKNOWN) { + this.hasComparators = net.caffeinemc.mods.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracking.findNearbyComparators(this.level, this.worldPosition) ? COMPARATOR_PRESENT : COMPARATOR_ABSENT; + } + return this.hasComparators == COMPARATOR_PRESENT; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java b/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java index 5b28e257982c911f4d8ffe286dc125b3f9b64ee4..960bbd0b2e4b17768e0c152ec1160cfb17dd8065 100644 --- a/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java @@ -26,7 +26,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; -public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer { +public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity, net.caffeinemc.mods.lithium.common.block.entity.SetChangedHandlingBlockEntity, net.caffeinemc.mods.lithium.api.inventory.LithiumInventory { // DivineMC - lithium: sleeping_block_entity private static final int INGREDIENT_SLOT = 3; private static final int FUEL_SLOT = 4; private static final int[] SLOTS_FOR_UP = new int[]{3}; @@ -138,6 +138,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements } public static void serverTick(Level level, BlockPos pos, BlockState state, BrewingStandBlockEntity blockEntity) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) blockEntity.checkSleep(state); // DivineMC - lithium: sleeping_block_entity ItemStack itemStack = blockEntity.items.get(4); if (blockEntity.fuel <= 0 && itemStack.is(ItemTags.BREWING_FUEL)) { // CraftBukkit start @@ -155,6 +156,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements itemStack.shrink(1); } // CraftBukkit end + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) blockEntity.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity setChanged(level, pos, state); } @@ -169,7 +171,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements } else if (!isBrewable || !itemStack1.is(blockEntity.ingredient)) { blockEntity.brewTime = 0; } - + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) blockEntity.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity setChanged(level, pos, state); } else if (isBrewable && blockEntity.fuel > 0) { blockEntity.fuel--; @@ -182,6 +184,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements blockEntity.brewTime = event.getBrewingTime(); // 400 -> event.getTotalBrewTime() // Paper - use brewing time from event // CraftBukkit end blockEntity.ingredient = itemStack1.getItem(); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) blockEntity.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity setChanged(level, pos, state); } @@ -288,6 +291,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements } this.fuel = input.getByteOr("Fuel", (byte)0); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.isSleeping() && this.level != null) this.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity } @Override @@ -334,4 +338,53 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements protected AbstractContainerMenu createMenu(int id, Inventory player) { return new BrewingStandMenu(id, player, this, this.dataAccess); } + + // DivineMC start - lithium: sleeping_block_entity + private net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + @Override + public net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { + return tickWrapper; + } + + @Override + public void lithium$setTickWrapper(net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper) { + this.tickWrapper = tickWrapper; + this.lithium$setSleepingTicker(null); + } + + @Override + public TickingBlockEntity lithium$getSleepingTicker() { + return sleepingTicker; + } + + @Override + public void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + + private void checkSleep(BlockState state) { + if (this.brewTime == 0 && state.is(net.minecraft.world.level.block.Blocks.BREWING_STAND) && this.level != null) { + this.lithium$startSleeping(); + } + } + + @Override + public void lithium$handleSetChanged() { + if (this.isSleeping() && this.level != null) { + this.wakeUpNow(); + } + } + + @Override + public net.minecraft.core.NonNullList getInventoryLithium() { + return items; + } + + @Override + public void setInventoryLithium(net.minecraft.core.NonNullList inventory) { + items = inventory; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java index 78c4d1fc052d72f8599c82291d2233ec2e552137..306c5bf630b295d27e5b1885bd784bbfbc846812 100644 --- a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java +++ b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java @@ -39,7 +39,7 @@ import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import org.slf4j.Logger; -public class CampfireBlockEntity extends BlockEntity implements Clearable { +public class CampfireBlockEntity extends BlockEntity implements Clearable, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity { private static final Logger LOGGER = LogUtils.getLogger(); private static final int BURN_COOL_SPEED = 2; private static final int NUM_SLOTS = 4; @@ -113,7 +113,11 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { if (flag) { setChanged(level, pos, state); + // DivineMC start - lithium: sleeping_block_entity + } else if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) { + campfire.lithium$startSleeping(); } + // DivineMC end - lithium: sleeping_block_entity } public static void cooldownTick(Level level, BlockPos pos, BlockState state, CampfireBlockEntity blockEntity) { @@ -128,7 +132,11 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { if (flag) { setChanged(level, pos, state); + // DivineMC start - lithium: sleeping_block_entity + } else if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) { + blockEntity.lithium$startSleeping(); } + // DivineMC end - lithium: sleeping_block_entity } public static void particleTick(Level level, BlockPos pos, BlockState state, CampfireBlockEntity blockEntity) { @@ -184,6 +192,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { System.arraycopy(cookingState, 0, this.stopCooking, 0, Math.min(this.stopCooking.length, bytes.capacity())); }); // Paper end - Add more Campfire API + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity } @Override @@ -238,6 +247,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { this.cookingTime[i] = event.getTotalCookTime(); // i -> event.getTotalCookTime() // CraftBukkit end this.cookingProgress[i] = 0; + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity this.items.set(i, stack.consumeAndReturn(1, entity)); level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(entity, this.getBlockState())); this.markUpdated(); @@ -281,4 +291,30 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { public void removeComponentsFromTag(ValueOutput output) { output.discard("Items"); } + + // DivineMC start - lithium: sleeping_block_entity + private net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + @Override + public net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { + return tickWrapper; + } + + @Override + public void lithium$setTickWrapper(net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper) { + this.tickWrapper = tickWrapper; + this.lithium$setSleepingTicker(null); + } + + @Override + public TickingBlockEntity lithium$getSleepingTicker() { + return sleepingTicker; + } + + @Override + public void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/net/minecraft/world/level/block/entity/ChestBlockEntity.java index 6b1299c20573ca009f6bffce829a4a593f548fce..e36049d044d7adc7e587c1c825fba36ce8f37089 100644 --- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java +++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java @@ -25,7 +25,7 @@ import net.minecraft.world.level.block.state.properties.ChestType; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; -public class ChestBlockEntity extends RandomizableContainerBlockEntity implements LidBlockEntity { +public class ChestBlockEntity extends RandomizableContainerBlockEntity implements LidBlockEntity, net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeEmitter, net.caffeinemc.mods.lithium.common.block.entity.SetBlockStateHandlingBlockEntity, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity, net.caffeinemc.mods.lithium.api.inventory.LithiumInventory { // DivineMC - lithium: sleeping_block_entity private static final int EVENT_SET_OPEN_COUNT = 1; public static final Component DEFAULT_NAME = Component.translatable("container.chest"); private NonNullList items = NonNullList.withSize(27, ItemStack.EMPTY); @@ -133,6 +133,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement public static void lidAnimateTick(Level level, BlockPos pos, BlockState state, ChestBlockEntity blockEntity) { blockEntity.chestLidController.tickLid(); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) blockEntity.checkSleep(); // DivineMC - lithium: sleeping_block_entity } public static void playSound(Level level, BlockPos pos, BlockState state, SoundEvent sound) { @@ -154,6 +155,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement @Override public boolean triggerEvent(int id, int type) { if (id == 1) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.sleepingTicker != null) this.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity this.chestLidController.shouldBeOpen(type > 0); return true; } else { @@ -189,6 +191,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement @Override protected void setItems(NonNullList items) { this.items = items; + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.lithium$emitStackListReplaced(); // DivineMC - lithium: sleeping_block_entity } @Override @@ -229,4 +232,51 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement Block block = state.getBlock(); level.blockEvent(pos, block, 1, eventParam); } + + // DivineMC start - lithium: sleeping_block_entity + private net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + private void checkSleep() { + if (this.getOpenNess(0.0F) == this.getOpenNess(1.0F)) { + this.lithium$startSleeping(); + } + } + + @Override + public net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { + return this.tickWrapper; + } + + @Override + public void lithium$setTickWrapper(net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper) { + this.tickWrapper = tickWrapper; + } + + @Override + public TickingBlockEntity lithium$getSleepingTicker() { + return this.sleepingTicker; + } + + @Override + public void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + + @Override + public void lithium$handleSetBlockState() { + //Handle switching double / single chest state + this.lithium$emitRemoved(); + } + + @Override + public net.minecraft.core.NonNullList getInventoryLithium() { + return items; + } + + @Override + public void setInventoryLithium(net.minecraft.core.NonNullList inventory) { + items = inventory; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java b/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java index 25cde0c41ef94f9fb602ed346baad7b4b0ae77f9..c2ee7d85356a7ba4dc53197d2bb0911ad971d82e 100644 --- a/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java +++ b/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java @@ -22,7 +22,7 @@ import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import org.slf4j.Logger; -public class ChiseledBookShelfBlockEntity extends BlockEntity implements ListBackedContainer { +public class ChiseledBookShelfBlockEntity extends BlockEntity implements ListBackedContainer, net.caffeinemc.mods.lithium.api.inventory.LithiumTransferConditionInventory { // DivineMC - lithium: sleeping_block_entity public static final int MAX_BOOKS_IN_STORAGE = 6; private static final Logger LOGGER = LogUtils.getLogger(); private static final int DEFAULT_LAST_INTERACTED_SLOT = -1; @@ -171,4 +171,11 @@ public class ChiseledBookShelfBlockEntity extends BlockEntity implements ListBac public void removeComponentsFromTag(ValueOutput output) { output.discard("Items"); } + + // DivineMC start - lithium: sleeping_block_entity + @Override + public boolean lithium$itemInsertionTestRequiresStackSize1() { + return true; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/CrafterBlockEntity.java b/net/minecraft/world/level/block/entity/CrafterBlockEntity.java index ffb5f2a07afcd6a3df0442ba48faa1557184d27c..8db1c47dd2f5c2165efc88954c6bb53015e9a4cf 100644 --- a/net/minecraft/world/level/block/entity/CrafterBlockEntity.java +++ b/net/minecraft/world/level/block/entity/CrafterBlockEntity.java @@ -23,7 +23,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; -public class CrafterBlockEntity extends RandomizableContainerBlockEntity implements CraftingContainer { +public class CrafterBlockEntity extends RandomizableContainerBlockEntity implements CraftingContainer, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity, net.caffeinemc.mods.lithium.common.block.entity.SetChangedHandlingBlockEntity { // DivineMC - lithium: sleeping_block_entity public static final int CONTAINER_WIDTH = 3; public static final int CONTAINER_HEIGHT = 3; public static final int CONTAINER_SIZE = 9; @@ -171,6 +171,7 @@ public class CrafterBlockEntity extends RandomizableContainerBlockEntity impleme } }); this.containerData.set(9, input.getIntOr("triggered", 0)); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.isSleeping() && this.level != null) this.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity } @Override @@ -280,10 +281,12 @@ public class CrafterBlockEntity extends RandomizableContainerBlockEntity impleme level.setBlock(pos, state.setValue(CrafterBlock.CRAFTING, false), Block.UPDATE_ALL); } } + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && i < 0) crafter.checkSleep(); // DivineMC - lithium: sleeping_block_entity } public void setCraftingTicksRemaining(int craftingTicksRemaining) { this.craftingTicksRemaining = craftingTicksRemaining; + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.isSleeping() && this.level != null) this.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity } public int getRedstoneSignal() { @@ -302,4 +305,43 @@ public class CrafterBlockEntity extends RandomizableContainerBlockEntity impleme private boolean slotCanBeDisabled(int slot) { return slot > -1 && slot < 9 && this.items.get(slot).isEmpty(); } + + // DivineMC start - lithium: sleeping_block_entity + private net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + @Override + public net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { + return this.tickWrapper; + } + + @Override + public void lithium$setTickWrapper(net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper) { + this.tickWrapper = tickWrapper; + this.lithium$setSleepingTicker(null); + } + + @Override + public TickingBlockEntity lithium$getSleepingTicker() { + return this.sleepingTicker; + } + + @Override + public void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + + private void checkSleep() { + if (this.craftingTicksRemaining == 0) { + this.lithium$startSleeping(); + } + } + + @Override + public void lithium$handleSetChanged() { + if (this.isSleeping() && this.level != null && !this.level.isClientSide) { + this.wakeUpNow(); + } + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/DispenserBlockEntity.java b/net/minecraft/world/level/block/entity/DispenserBlockEntity.java index b4a155cc914092dad83977df714fbbc033c69d19..f3cd91636539ebd201f6a22ca5a4f8a8b78d643d 100644 --- a/net/minecraft/world/level/block/entity/DispenserBlockEntity.java +++ b/net/minecraft/world/level/block/entity/DispenserBlockEntity.java @@ -13,7 +13,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; -public class DispenserBlockEntity extends RandomizableContainerBlockEntity { +public class DispenserBlockEntity extends RandomizableContainerBlockEntity implements net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, net.caffeinemc.mods.lithium.api.inventory.LithiumInventory { // DivineMC - lithium: sleeping_block_entity public static final int CONTAINER_SIZE = 9; private static final Component DEFAULT_NAME = Component.translatable("container.dispenser"); private NonNullList items = NonNullList.withSize(9, ItemStack.EMPTY); @@ -135,10 +135,23 @@ public class DispenserBlockEntity extends RandomizableContainerBlockEntity { @Override protected void setItems(NonNullList items) { this.items = items; + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.lithium$emitStackListReplaced(); // DivineMC - lithium: sleeping_block_entity } @Override protected AbstractContainerMenu createMenu(int id, Inventory player) { return new DispenserMenu(id, player, this); } + + // DivineMC start - lithium: sleeping_block_entity + @Override + public net.minecraft.core.NonNullList getInventoryLithium() { + return items; + } + + @Override + public void setInventoryLithium(net.minecraft.core.NonNullList inventory) { + items = inventory; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java index 775928cfe700202a70b19589ca72afd9768b62f1..2eda08301a3e3888c55a21390d1a718b86d50463 100644 --- a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java +++ b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java @@ -10,7 +10,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; -public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity { +public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity { // DivineMC - lithium: sleeping_block_entity private final ChestLidController chestLidController = new ChestLidController(); public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() { @Override @@ -58,11 +58,13 @@ public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity public static void lidAnimateTick(Level level, BlockPos pos, BlockState state, EnderChestBlockEntity blockEntity) { blockEntity.chestLidController.tickLid(); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) blockEntity.checkSleep(); // DivineMC - lithium: sleeping_block_entity } @Override public boolean triggerEvent(int id, int type) { if (id == 1) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.sleepingTicker != null) this.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity this.chestLidController.shouldBeOpen(type > 0); return true; } else { @@ -99,4 +101,35 @@ public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity public float getOpenNess(float partialTick) { return this.chestLidController.getOpenness(partialTick); } + + // DivineMC start - lithium: sleeping_block_entity + private net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + @Override + public net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { + return this.tickWrapper; + } + + @Override + public void lithium$setTickWrapper(net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper) { + this.tickWrapper = tickWrapper; + } + + @Override + public TickingBlockEntity lithium$getSleepingTicker() { + return this.sleepingTicker; + } + + @Override + public void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + + private void checkSleep() { + if (this.getOpenNess(0.0F) == this.getOpenNess(1.0F)) { + this.lithium$startSleeping(); + } + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java index daf4f28d04a1ad1e034c6d7dedba9e4e2272f2ea..3afb0ce74c5378ce909c1a1e6182aaae5fddefb4 100644 --- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java +++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java @@ -28,7 +28,7 @@ import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.AABB; -public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper { +public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity, net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementListener, net.caffeinemc.mods.lithium.api.inventory.LithiumInventory, net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener, net.caffeinemc.mods.lithium.common.hopper.UpdateReceiver, net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker { // DivineMC - lithium: sleeping_block_entity public static final int MOVE_ITEM_SPEED = 8; public static final int HOPPER_CONTAINER_SIZE = 5; private static final int[][] CACHED_SLOTS = new int[54][]; @@ -119,6 +119,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Override public void setBlockState(BlockState blockState) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.level != null && !this.level.isClientSide() && blockState.getValue(HopperBlock.FACING) != this.getBlockState().getValue(HopperBlock.FACING)) this.invalidateCachedData(); // DivineMC - lithium: sleeping_block_entity super.setBlockState(blockState); this.facing = blockState.getValue(HopperBlock.FACING); } @@ -137,6 +138,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen boolean result = tryMoveItems(level, pos, state, blockEntity, () -> { return suckInItems(level, blockEntity); }); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) blockEntity.checkSleepingConditions(); // DivineMC - lithium: sleeping_block_entity if (!result && blockEntity.level.spigotConfig.hopperCheck > 1) { blockEntity.setCooldown(blockEntity.level.spigotConfig.hopperCheck); } @@ -199,6 +201,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen if (flag) { blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot setChanged(level, pos, state); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && !blockEntity.isOnCooldown() && !blockEntity.isSleeping() && !state.getValue(HopperBlock.ENABLED)) blockEntity.lithium$startSleeping(); // DivineMC - lithium: sleeping_block_entity return true; } } @@ -375,6 +378,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen private static void applyCooldown(final Hopper hopper) { if (hopper instanceof HopperBlockEntity blockEntity && blockEntity.getLevel() != null) { blockEntity.setCooldown(blockEntity.getLevel().spigotConfig.hopperTransfer); + blockEntity.skipNextSleepCheckAfterCooldown = true; // DivineMC - lithium: sleeping_block_entity } } @@ -418,11 +422,19 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen // Paper end - Perf: Optimize Hoppers private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) { - Container attachedContainer = getAttachedContainer(level, pos, blockEntity); + Container attachedContainer = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity ? blockEntity.getInsertInventory(level) : getAttachedContainer(level, pos, blockEntity); // DivineMC - lithium: sleeping_block_entity if (attachedContainer == null) { return false; } else { Direction opposite = blockEntity.facing.getOpposite(); + // DivineMC start - lithium: sleeping_block_entity + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) { + Boolean res = lithiumInsert(level, pos, blockEntity, attachedContainer); + if (res != null) { + return res; + } + } + // DivineMC end - lithium: sleeping_block_entity if (isFullContainer(attachedContainer, opposite)) { // DivineMC start - SparklyPaper: Allow throttling hopper checks if the target container is full if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.hopperThrottleWhenFull && org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.hopperThrottleSkipTicks > 0) { @@ -536,10 +548,18 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen public static boolean suckInItems(Level level, Hopper hopper) { BlockPos blockPos = BlockPos.containing(hopper.getLevelX(), hopper.getLevelY() + 1.0, hopper.getLevelZ()); BlockState blockState = level.getBlockState(blockPos); - Container sourceContainer = getSourceContainer(level, hopper, blockPos, blockState); + Container sourceContainer = org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity ? getExtractInventory(level, hopper, blockPos, blockState) : getSourceContainer(level, hopper, blockPos, blockState); // DivineMC - lithium: sleeping_block_entity if (sourceContainer != null) { Direction direction = Direction.DOWN; skipPullModeEventFire = skipHopperEvents; // Paper - Perf: Optimize Hoppers + // DivineMC start - lithium: sleeping_block_entity + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) { + Boolean res = lithiumExtract(level, hopper, sourceContainer); + if (res != null) { + return res; + } + } + // DivineMC end - lithium: sleeping_block_entity for (int i : getSlots(sourceContainer, direction)) { if (tryTakeInItemFromSlot(hopper, sourceContainer, i, direction, level)) { // Spigot @@ -551,7 +571,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen } else { boolean flag = hopper.isGridAligned() && blockState.isCollisionShapeFullBlock(level, blockPos) && !blockState.is(BlockTags.DOES_NOT_BLOCK_HOPPERS); if (!flag) { - for (ItemEntity itemEntity : getItemsAtAndAbove(level, hopper)) { + for (ItemEntity itemEntity : org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity ? lithiumGetInputItemEntities(level, hopper) : getItemsAtAndAbove(level, hopper)) { // DivineMC - lithium: sleeping_block_entity if (addItem(hopper, itemEntity)) { return true; } @@ -721,7 +741,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen // CraftBukkit start @Nullable private static Container runHopperInventorySearchEvent( - Container container, + @Nullable Container container, org.bukkit.craftbukkit.block.CraftBlock hopper, org.bukkit.craftbukkit.block.CraftBlock searchLocation, org.bukkit.event.inventory.HopperInventorySearchEvent.ContainerType containerType @@ -849,6 +869,19 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen } public void setCooldown(int cooldownTime) { + // DivineMC start - lithium: sleeping_block_entity + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) { + if (cooldownTime == 7) { + if (this.tickedGameTime == Long.MAX_VALUE) { + this.sleepOnlyCurrentTick(); + } else { + this.wakeUpNow(); + } + } else if (cooldownTime > 0 && this.sleepingTicker != null) { + this.wakeUpNow(); + } + } + // DivineMC end - lithium: sleeping_block_entity this.cooldownTime = cooldownTime; } @@ -868,6 +901,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Override protected void setItems(NonNullList items) { this.items = items; + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.lithium$emitStackListReplaced(); // DivineMC - lithium: sleeping_block_entity } public static void entityInside(Level level, BlockPos pos, BlockState state, Entity entity, HopperBlockEntity blockEntity) { @@ -882,4 +916,749 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen protected AbstractContainerMenu createMenu(int id, Inventory player) { return new HopperMenu(id, player, this); } + + // DivineMC start - lithium: sleeping_block_entity + @Nullable private net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + @Nullable private TickingBlockEntity sleepingTicker = null; + private long myModCountAtLastInsert, myModCountAtLastExtract, myModCountAtLastItemCollect; + private boolean skipNextSleepCheckAfterCooldown = false; + + private net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory insertionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.UNKNOWN; + private net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory extractionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.UNKNOWN; + + @Nullable + private Container insertBlockInventory, extractBlockInventory; + + @Nullable + private net.caffeinemc.mods.lithium.api.inventory.LithiumInventory insertInventory, extractInventory; + @Nullable + private net.caffeinemc.mods.lithium.common.hopper.LithiumStackList insertStackList, extractStackList; + private long insertStackListModCount, extractStackListModCount; + + @Nullable + private List collectItemEntityTracker; + private boolean collectItemEntityTrackerWasEmpty; + @Nullable + private AABB collectItemEntityBox; + private long collectItemEntityAttemptTime; + + @Nullable + private List extractInventoryEntityTracker; + @Nullable + private AABB extractInventoryEntityBox; + private long extractInventoryEntityFailedSearchTime; + + @Nullable + private List insertInventoryEntityTracker; + @Nullable + private AABB insertInventoryEntityBox; + private long insertInventoryEntityFailedSearchTime; + + private boolean shouldCheckSleep; + + private void checkSleepingConditions() { + if (this.cooldownTime > 0 || this.getLevel() == null || skipNextSleepCheckAfterCooldown) { + return; + } + if (isSleeping()) { + return; + } + if (!this.shouldCheckSleep) { + this.shouldCheckSleep = true; + return; + } + boolean listenToExtractTracker = false; + boolean listenToInsertTracker = false; + boolean listenToExtractEntities = false; + boolean listenToItemEntities = false; + boolean listenToInsertEntities = false; + + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList thisStackList = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(this); + + if (this.extractionMode != net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE && thisStackList.getFullSlots() != thisStackList.size()) { + if (this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + Container blockInventory = this.extractBlockInventory; + if (this.extractStackList != null && + blockInventory instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) { + if (!this.extractStackList.maybeSendsComparatorUpdatesOnFailedExtract() || (blockInventory instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracker comparatorTracker && !comparatorTracker.lithium$hasAnyComparatorNearby())) { + listenToExtractTracker = true; + } else { + return; + } + } else { + return; + } + } else if (this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY) { + BlockState hopperState = this.getBlockState(); + listenToExtractEntities = true; + + BlockPos blockPos = this.getBlockPos().above(); + BlockState blockState = this.getLevel().getBlockState(blockPos); + if (!blockState.isCollisionShapeFullBlock(this.getLevel(), blockPos) || blockState.is(BlockTags.DOES_NOT_BLOCK_HOPPERS)) { + listenToItemEntities = true; + } + } else { + return; + } + } + if (this.insertionMode != net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE && 0 < thisStackList.getOccupiedSlots()) { + if (this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + Container blockInventory = this.insertBlockInventory; + if (this.insertStackList != null && blockInventory instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) { + listenToInsertTracker = true; + } else { + return; + } + } else if (this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY) { + BlockState hopperState = this.getBlockState(); + listenToInsertEntities = true; + } else { + return; + } + } + + if (listenToExtractTracker) { + ((net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) this.extractBlockInventory).listenForContentChangesOnce(this.extractStackList, this); + } + if (listenToInsertTracker) { + ((net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) this.insertBlockInventory).listenForContentChangesOnce(this.insertStackList, this); + } + if (listenToInsertEntities) { + if (this.insertInventoryEntityTracker == null || this.insertInventoryEntityTracker.isEmpty()) { + return; + } + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.listenToEntityMovementOnce(this, insertInventoryEntityTracker); + } + if (listenToExtractEntities) { + if (this.extractInventoryEntityTracker == null || this.extractInventoryEntityTracker.isEmpty()) { + return; + } + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.listenToEntityMovementOnce(this, extractInventoryEntityTracker); + } + if (listenToItemEntities) { + if (this.collectItemEntityTracker == null || this.collectItemEntityTracker.isEmpty()) { + return; + } + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.listenToEntityMovementOnce(this, collectItemEntityTracker); + } + + this.listenForContentChangesOnce(thisStackList, this); + lithium$startSleeping(); + } + + @Override + public void lithium$setSleepingTicker(@Nullable TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + + @Override + public @Nullable TickingBlockEntity lithium$getSleepingTicker() { + return sleepingTicker; + } + + @Override + public void lithium$setTickWrapper(net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper) { + this.tickWrapper = tickWrapper; + this.lithium$setSleepingTicker(null); + } + + @Override + public @Nullable net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { + return tickWrapper; + } + + @Override + public boolean lithium$startSleeping() { + if (this.isSleeping()) { + return false; + } + + net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = this.lithium$getTickWrapper(); + if (tickWrapper != null) { + this.lithium$setSleepingTicker(tickWrapper.ticker); + tickWrapper.rebind(net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity.SLEEPING_BLOCK_ENTITY_TICKER); + + // Set the last tick time to max value, so other hoppers transferring into this hopper will set it to 7gt + // cooldown. Then when waking up, we make sure to not tick this hopper in the same gametick. + // This makes the observable hopper cooldown not be different from vanilla. + this.tickedGameTime = Long.MAX_VALUE; + return true; + } + return false; + } + + @Override + public void handleEntityMovement() { + this.wakeUpNow(); + } + + @Override + public NonNullList getInventoryLithium() { + return items; + } + + @Override + public void setInventoryLithium(NonNullList inventory) { + this.items = inventory; + } + + @Override + public void lithium$handleInventoryContentModified(Container inventory) { + wakeUpNow(); + } + + @Override + public void lithium$handleInventoryRemoved(Container inventory) { + wakeUpNow(); + if (inventory == this.insertBlockInventory) { + this.invalidateBlockInsertionData(); + } + if (inventory == this.extractBlockInventory) { + this.invalidateBlockExtractionData(); + } + if (inventory == this) { + this.invalidateCachedData(); + } + } + + @Override + public boolean lithium$handleComparatorAdded(Container inventory) { + if (inventory == this.extractBlockInventory) { + wakeUpNow(); + return true; + } + return false; + } + + @Override + public void lithium$invalidateCacheOnNeighborUpdate(boolean fromAbove) { + if (fromAbove) { + if (this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY || this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE) { + this.invalidateBlockExtractionData(); + } + } else { + if (this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY || this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE) { + this.invalidateBlockInsertionData(); + } + } + } + + @Override + public void lithium$invalidateCacheOnUndirectedNeighborUpdate() { + if (this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY || this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE) { + this.invalidateBlockExtractionData(); + } + if (this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY || this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE) { + this.invalidateBlockInsertionData(); + } + } + + @Override + public void lithium$invalidateCacheOnNeighborUpdate(Direction fromDirection) { + boolean fromAbove = fromDirection == Direction.UP; + if (fromAbove || this.getBlockState().getValue(HopperBlock.FACING) == fromDirection) { + this.lithium$invalidateCacheOnNeighborUpdate(fromAbove); + } + } + + private void invalidateBlockInsertionData() { + this.insertionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.UNKNOWN; + this.insertBlockInventory = null; + this.insertInventory = null; + this.insertStackList = null; + this.insertStackListModCount = 0; + + wakeUpNow(); + } + + private void invalidateCachedData() { + this.shouldCheckSleep = false; + this.invalidateInsertionData(); + this.invalidateExtractionData(); + } + + private void invalidateInsertionData() { + if (this.level instanceof net.minecraft.server.level.ServerLevel) { + if (this.insertInventoryEntityTracker != null) { + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.unregister(this.insertInventoryEntityTracker); + this.insertInventoryEntityTracker = null; + this.insertInventoryEntityBox = null; + this.insertInventoryEntityFailedSearchTime = 0L; + } + } + + if (this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + assert this.insertBlockInventory != null; + ((net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) this.insertBlockInventory).stopListenForMajorInventoryChanges(this); + } + this.invalidateBlockInsertionData(); + } + + private void invalidateExtractionData() { + if (this.level instanceof net.minecraft.server.level.ServerLevel) { + if (this.extractInventoryEntityTracker != null) { + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.unregister(this.extractInventoryEntityTracker); + this.extractInventoryEntityTracker = null; + this.extractInventoryEntityBox = null; + this.extractInventoryEntityFailedSearchTime = 0L; + } + if (this.collectItemEntityTracker != null) { + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.unregister(this.collectItemEntityTracker); + this.collectItemEntityTracker = null; + this.collectItemEntityBox = null; + this.collectItemEntityTrackerWasEmpty = false; + } + } + if (this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + assert this.extractBlockInventory != null; + ((net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) this.extractBlockInventory).stopListenForMajorInventoryChanges(this); + } + this.invalidateBlockExtractionData(); + } + + private void invalidateBlockExtractionData() { + this.extractionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.UNKNOWN; + this.extractBlockInventory = null; + this.extractInventory = null; + this.extractStackList = null; + this.extractStackListModCount = 0; + + this.wakeUpNow(); + } + + private static @Nullable Container getExtractInventory(Level world, Hopper hopper, BlockPos extractBlockPos, BlockState extractBlockState) { + if (!(hopper instanceof HopperBlockEntity hopperBlockEntity)) { + return getSourceContainer(world, hopper, extractBlockPos, extractBlockState); //Hopper Minecarts do not cache Inventories + } + + Container blockInventory = hopperBlockEntity.lithium$getExtractBlockInventory(world, extractBlockPos, extractBlockState); + if (blockInventory == null) { + blockInventory = hopperBlockEntity.lithium$getExtractEntityInventory(world); + } + return org.bukkit.event.inventory.HopperInventorySearchEvent.getHandlerList().getRegisteredListeners().length == 0 ? blockInventory : runHopperInventorySearchEvent( + blockInventory, + org.bukkit.craftbukkit.block.CraftBlock.at(world, hopperBlockEntity.getBlockPos()), + org.bukkit.craftbukkit.block.CraftBlock.at(world, extractBlockPos), + org.bukkit.event.inventory.HopperInventorySearchEvent.ContainerType.SOURCE + ); + } + + public @Nullable Container lithium$getExtractBlockInventory(Level world, BlockPos extractBlockPos, BlockState extractBlockState) { + Container blockInventory = this.extractBlockInventory; + if (this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY) { + return null; + } else if (this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE) { + return blockInventory; + } else if (this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + return blockInventory; + } else if (this.extractionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_ENTITY) { + BlockEntity blockEntity = (BlockEntity) java.util.Objects.requireNonNull(blockInventory); + BlockPos pos = blockEntity.getBlockPos(); + if (!(blockEntity).isRemoved() && pos.equals(extractBlockPos)) { + net.caffeinemc.mods.lithium.api.inventory.LithiumInventory optimizedInventory; + if ((optimizedInventory = this.extractInventory) != null) { + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList insertInventoryStackList = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(optimizedInventory); + if (insertInventoryStackList == this.extractStackList) { + return optimizedInventory; + } else { + this.invalidateBlockExtractionData(); + } + } else { + return blockInventory; + } + } + } + + blockInventory = getBlockContainer(world, extractBlockPos, extractBlockState); + blockInventory = net.caffeinemc.mods.lithium.common.hopper.HopperHelper.replaceDoubleInventory(blockInventory); + this.cacheExtractBlockInventory(blockInventory); + return blockInventory; + } + + public @Nullable Container lithium$getInsertBlockInventory(Level world) { + Container blockInventory = this.insertBlockInventory; + if (this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY) { + return null; + } else if (this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE) { + return blockInventory; + } else if (this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + return blockInventory; + } else if (this.insertionMode == net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_ENTITY) { + BlockEntity blockEntity = (BlockEntity) java.util.Objects.requireNonNull(blockInventory); + //Movable Block Entity compatibility - position comparison + BlockPos pos = blockEntity.getBlockPos(); + Direction direction = this.facing; + BlockPos transferPos = this.getBlockPos().relative(direction); + if (!(blockEntity).isRemoved() && + pos.equals(transferPos)) { + net.caffeinemc.mods.lithium.api.inventory.LithiumInventory optimizedInventory; + if ((optimizedInventory = this.insertInventory) != null) { + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList insertInventoryStackList = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(optimizedInventory); + //This check is necessary as sometimes the stacklist is silently replaced (e.g. command making furnace read inventory from nbt) + if (insertInventoryStackList == this.insertStackList) { + return optimizedInventory; + } else { + this.invalidateBlockInsertionData(); + } + } else { + return blockInventory; + } + } + } + + //No Cached Inventory: Get like vanilla and cache + Direction direction = this.facing; + BlockPos insertBlockPos = this.getBlockPos().relative(direction); + BlockState blockState = world.getBlockState(insertBlockPos); + blockInventory = getBlockContainer(world, insertBlockPos, blockState); + blockInventory = net.caffeinemc.mods.lithium.common.hopper.HopperHelper.replaceDoubleInventory(blockInventory); + this.cacheInsertBlockInventory(blockInventory); + return blockInventory; + } + + public @Nullable Container getInsertInventory(Level world) { + Container blockInventory = getInsertInventory0(world); + return org.bukkit.event.inventory.HopperInventorySearchEvent.getHandlerList().getRegisteredListeners().length == 0 ? blockInventory : runHopperInventorySearchEvent( + blockInventory, + org.bukkit.craftbukkit.block.CraftBlock.at(world, this.getBlockPos()), + org.bukkit.craftbukkit.block.CraftBlock.at(world, this.getBlockPos().relative(this.facing)), + org.bukkit.event.inventory.HopperInventorySearchEvent.ContainerType.DESTINATION + ); + } + + public @Nullable Container getInsertInventory0(Level world) { + Container blockInventory = this.lithium$getInsertBlockInventory(world); + if (blockInventory != null) { + return blockInventory; + } + + if (this.insertInventoryEntityTracker == null) { + this.initInsertInventoryTracker(world); + } + if (net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.isUnchangedSince(this.insertInventoryEntityFailedSearchTime, this.insertInventoryEntityTracker)) { + this.insertInventoryEntityFailedSearchTime = this.tickedGameTime; + return null; + } + this.insertInventoryEntityFailedSearchTime = Long.MIN_VALUE; + this.shouldCheckSleep = false; + + List inventoryEntities = net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker.getEntities(world, this.insertInventoryEntityBox); + if (inventoryEntities.isEmpty()) { + this.insertInventoryEntityFailedSearchTime = this.tickedGameTime; + //Remember failed entity search timestamp. This allows shortcutting if no entity movement happens. + return null; + } + Container inventory = inventoryEntities.get(world.random.nextInt(inventoryEntities.size())); + if (inventory instanceof net.caffeinemc.mods.lithium.api.inventory.LithiumInventory optimizedInventory) { + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList insertInventoryStackList = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(optimizedInventory); + if (inventory != this.insertInventory || this.insertStackList != insertInventoryStackList) { + this.cacheInsertLithiumInventory(optimizedInventory); + } + } + + return inventory; + } + + private void initCollectItemEntityTracker() { + assert this.level instanceof net.minecraft.server.level.ServerLevel; + AABB inputBox = this.getSuckAabb().move(this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ()); + this.collectItemEntityBox = inputBox; + this.collectItemEntityTracker = + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionItemEntityMovementTracker.registerAt( + (net.minecraft.server.level.ServerLevel) this.level, + inputBox + ); + this.collectItemEntityAttemptTime = Long.MIN_VALUE; + } + + private void initExtractInventoryTracker(Level world) { + assert world instanceof net.minecraft.server.level.ServerLevel; + BlockPos pos = this.worldPosition.relative(Direction.UP); + this.extractInventoryEntityBox = new AABB(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1); + this.extractInventoryEntityTracker = + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker.registerAt( + (net.minecraft.server.level.ServerLevel) this.level, + this.extractInventoryEntityBox + ); + this.extractInventoryEntityFailedSearchTime = Long.MIN_VALUE; + } + + private void initInsertInventoryTracker(Level world) { + assert world instanceof net.minecraft.server.level.ServerLevel; + Direction direction = this.facing; + BlockPos pos = this.worldPosition.relative(direction); + this.insertInventoryEntityBox = new AABB(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1); + this.insertInventoryEntityTracker = + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker.registerAt( + (net.minecraft.server.level.ServerLevel) this.level, + this.insertInventoryEntityBox + ); + this.insertInventoryEntityFailedSearchTime = Long.MIN_VALUE; + } + + private @Nullable Container lithium$getExtractEntityInventory(Level world) { + if (this.extractInventoryEntityTracker == null) { + this.initExtractInventoryTracker(world); + } + if (net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.isUnchangedSince(this.extractInventoryEntityFailedSearchTime, this.extractInventoryEntityTracker)) { + this.extractInventoryEntityFailedSearchTime = this.tickedGameTime; + return null; + } + this.extractInventoryEntityFailedSearchTime = Long.MIN_VALUE; + this.shouldCheckSleep = false; + + List inventoryEntities = net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker.getEntities(world, this.extractInventoryEntityBox); + if (inventoryEntities.isEmpty()) { + this.extractInventoryEntityFailedSearchTime = this.tickedGameTime; + //only set unchanged when no entity present. this allows shortcutting this case + //shortcutting the entity present case requires checking its change counter + return null; + } + Container inventory = inventoryEntities.get(world.random.nextInt(inventoryEntities.size())); + if (inventory instanceof net.caffeinemc.mods.lithium.api.inventory.LithiumInventory optimizedInventory) { + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList extractInventoryStackList = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(optimizedInventory); + if (inventory != this.extractInventory || this.extractStackList != extractInventoryStackList) { + //not caching the inventory (NO_BLOCK_INVENTORY prevents it) + //make change counting on the entity inventory possible, without caching it as block inventory + this.cacheExtractLithiumInventory(optimizedInventory); + } + } + return inventory; + } + + /** + * Makes this hopper remember the given inventory. + * + * @param insertInventory Block inventory / Blockentity inventory to be remembered + */ + private void cacheInsertBlockInventory(@Nullable Container insertInventory) { + assert !(insertInventory instanceof Entity); + if (insertInventory instanceof net.caffeinemc.mods.lithium.api.inventory.LithiumInventory optimizedInventory) { + this.cacheInsertLithiumInventory(optimizedInventory); + } else { + this.insertInventory = null; + this.insertStackList = null; + this.insertStackListModCount = 0; + } + + if (insertInventory instanceof BlockEntity || insertInventory instanceof net.minecraft.world.CompoundContainer) { + this.insertBlockInventory = insertInventory; + if (insertInventory instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) { + this.insertionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY; + ((net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) insertInventory).listenForMajorInventoryChanges(this); + } else { + this.insertionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_ENTITY; + } + } else { + if (insertInventory == null) { + this.insertBlockInventory = null; + this.insertionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY; + } else { + this.insertBlockInventory = insertInventory; + this.insertionMode = insertInventory instanceof net.caffeinemc.mods.lithium.common.hopper.BlockStateOnlyInventory ? net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE : net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.UNKNOWN; + } + } + } + + private void cacheInsertLithiumInventory(net.caffeinemc.mods.lithium.api.inventory.LithiumInventory optimizedInventory) { + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList insertInventoryStackList = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(optimizedInventory); + this.insertInventory = optimizedInventory; + this.insertStackList = insertInventoryStackList; + this.insertStackListModCount = insertInventoryStackList.getModCount() - 1; + } + + private void cacheExtractLithiumInventory(net.caffeinemc.mods.lithium.api.inventory.LithiumInventory optimizedInventory) { + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList extractInventoryStackList = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(optimizedInventory); + this.extractInventory = optimizedInventory; + this.extractStackList = extractInventoryStackList; + this.extractStackListModCount = extractInventoryStackList.getModCount() - 1; + } + + /** + * Makes this hopper remember the given inventory. + * + * @param extractInventory Block inventory / Blockentity inventory to be remembered + */ + private void cacheExtractBlockInventory(@Nullable Container extractInventory) { + assert !(extractInventory instanceof Entity); + if (extractInventory instanceof net.caffeinemc.mods.lithium.api.inventory.LithiumInventory optimizedInventory) { + this.cacheExtractLithiumInventory(optimizedInventory); + } else { + this.extractInventory = null; + this.extractStackList = null; + this.extractStackListModCount = 0; + } + + if (extractInventory instanceof BlockEntity || extractInventory instanceof net.minecraft.world.CompoundContainer) { + this.extractBlockInventory = extractInventory; + if (extractInventory instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) { + this.extractionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY; + ((net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker) extractInventory).listenForMajorInventoryChanges(this); + } else { + this.extractionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_ENTITY; + } + } else { + if (extractInventory == null) { + this.extractBlockInventory = null; + this.extractionMode = net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY; + } else { + this.extractBlockInventory = extractInventory; + this.extractionMode = extractInventory instanceof net.caffeinemc.mods.lithium.common.hopper.BlockStateOnlyInventory ? net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.BLOCK_STATE : net.caffeinemc.mods.lithium.common.hopper.HopperCachingState.BlockInventory.UNKNOWN; + } + } + } + + private static List lithiumGetInputItemEntities(Level world, Hopper hopper) { + if (!(hopper instanceof HopperBlockEntity hopperBlockEntity)) { + return getItemsAtAndAbove(world, hopper); //optimizations not implemented for hopper minecarts + } + + if (hopperBlockEntity.collectItemEntityTracker == null) { + hopperBlockEntity.initCollectItemEntityTracker(); + } + + long modCount = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(hopperBlockEntity).getModCount(); + + if ((hopperBlockEntity.collectItemEntityTrackerWasEmpty || hopperBlockEntity.myModCountAtLastItemCollect == modCount) && + net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker.isUnchangedSince(hopperBlockEntity.collectItemEntityAttemptTime, hopperBlockEntity.collectItemEntityTracker)) { + hopperBlockEntity.collectItemEntityAttemptTime = hopperBlockEntity.tickedGameTime; + return java.util.Collections.emptyList(); + } + + hopperBlockEntity.myModCountAtLastItemCollect = modCount; + hopperBlockEntity.shouldCheckSleep = false; + + List itemEntities = net.caffeinemc.mods.lithium.common.tracking.entity.ChunkSectionItemEntityMovementTracker.getEntities(world, hopperBlockEntity.collectItemEntityBox); + hopperBlockEntity.collectItemEntityAttemptTime = hopperBlockEntity.tickedGameTime; + hopperBlockEntity.collectItemEntityTrackerWasEmpty = itemEntities.isEmpty(); + //set unchanged so that if this extract fails and there is no other change to hoppers or items, extracting + // items can be skipped. + return itemEntities; + } + + private static @Nullable Boolean lithiumInsert(Level world, BlockPos pos, HopperBlockEntity hopperBlockEntity, @Nullable Container insertInventory) { + if (insertInventory == null || hopperBlockEntity instanceof net.minecraft.world.WorldlyContainer) { + //call the vanilla code to allow other mods inject features + //e.g. carpet mod allows hoppers to insert items into wool blocks + return null; + } + + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList hopperStackList = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(hopperBlockEntity); + if (hopperBlockEntity.insertInventory == insertInventory && hopperStackList.getModCount() == hopperBlockEntity.myModCountAtLastInsert) { + if (hopperBlockEntity.insertStackList != null && hopperBlockEntity.insertStackList.getModCount() == hopperBlockEntity.insertStackListModCount) { + //ComparatorUpdatePattern.NO_UPDATE.apply(hopperBlockEntity, hopperStackList); //commented because it's a noop, Hoppers do not send useless comparator updates + return false; + } + } + + boolean insertInventoryWasEmptyHopperNotDisabled = insertInventory instanceof HopperBlockEntity hopperInv && + !hopperInv.isOnCustomCooldown() && hopperBlockEntity.insertStackList != null && + hopperBlockEntity.insertStackList.getOccupiedSlots() == 0; + + boolean insertInventoryHandlesModdedCooldown = + insertInventory.canReceiveTransferCooldown() && + hopperBlockEntity.insertStackList != null ? + hopperBlockEntity.insertStackList.getOccupiedSlots() == 0 : + insertInventory.isEmpty(); + + skipPushModeEventFire = skipHopperEvents; + //noinspection ConstantConditions + if (!(hopperBlockEntity.insertInventory == insertInventory && hopperBlockEntity.insertStackList.getFullSlots() == hopperBlockEntity.insertStackList.size())) { + Direction fromDirection = hopperBlockEntity.facing.getOpposite(); + int size = hopperStackList.size(); + for (int i = 0; i < size; ++i) { + ItemStack transferStack = hopperStackList.get(i); + if (!transferStack.isEmpty()) { + if (!skipPushModeEventFire && canTakeItemFromContainer(insertInventory, hopperBlockEntity, transferStack, i, Direction.DOWN)) { + transferStack = callPushMoveEvent(insertInventory, transferStack, hopperBlockEntity); + if (transferStack == null) { // cancelled + break; + } + } + boolean transferSuccess = net.caffeinemc.mods.lithium.common.hopper.HopperHelper.tryMoveSingleItem(insertInventory, transferStack, fromDirection); + if (transferSuccess) { + if (insertInventoryWasEmptyHopperNotDisabled) { + HopperBlockEntity receivingHopper = (HopperBlockEntity) insertInventory; + int k = 8; + if (receivingHopper.tickedGameTime >= hopperBlockEntity.tickedGameTime) { + k = 7; + } + receivingHopper.setCooldown(k); + } + if (insertInventoryHandlesModdedCooldown) { + insertInventory.setTransferCooldown(hopperBlockEntity.tickedGameTime); + } + insertInventory.setChanged(); + return true; + } + } + } + } + hopperBlockEntity.myModCountAtLastInsert = hopperStackList.getModCount(); + if (hopperBlockEntity.insertStackList != null) { + hopperBlockEntity.insertStackListModCount = hopperBlockEntity.insertStackList.getModCount(); + } + return false; + } + + private static @Nullable Boolean lithiumExtract(Level world, Hopper to, Container from) { + if (!(to instanceof HopperBlockEntity hopperBlockEntity)) { + return null; //optimizations not implemented for hopper minecarts + } + + if (from != hopperBlockEntity.extractInventory || hopperBlockEntity.extractStackList == null) { + return null; //from inventory is not an optimized inventory, vanilla fallback + } + + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList hopperStackList = net.caffeinemc.mods.lithium.common.hopper.InventoryHelper.getLithiumStackList(hopperBlockEntity); + net.caffeinemc.mods.lithium.common.hopper.LithiumStackList fromStackList = hopperBlockEntity.extractStackList; + + if (hopperStackList.getModCount() == hopperBlockEntity.myModCountAtLastExtract) { + if (fromStackList.getModCount() == hopperBlockEntity.extractStackListModCount) { + if (!(from instanceof net.caffeinemc.mods.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracker comparatorTracker) || comparatorTracker.lithium$hasAnyComparatorNearby()) { + //noinspection CollectionAddedToSelf + fromStackList.runComparatorUpdatePatternOnFailedExtract(fromStackList, from); + } + return false; + } + } + + int[] availableSlots = from instanceof WorldlyContainer ? ((WorldlyContainer) from).getSlotsForFace(Direction.DOWN) : null; + int fromSize = availableSlots != null ? availableSlots.length : from.getContainerSize(); + for (int i = 0; i < fromSize; i++) { + int fromSlot = availableSlots != null ? availableSlots[i] : i; + ItemStack itemStack = fromStackList.get(fromSlot); + if (!itemStack.isEmpty() && canTakeItemFromContainer(to, from, itemStack, fromSlot, Direction.DOWN)) { + if (!skipPullModeEventFire) { + itemStack = callPullMoveEvent(to, from, itemStack); + if (itemStack == null) { // cancelled + return true; + } + } + //calling removeStack is necessary due to its side effects (markDirty in LootableContainerBlockEntity) + ItemStack takenItem = from.removeItem(fromSlot, 1); + assert !takenItem.isEmpty(); + boolean transferSuccess = net.caffeinemc.mods.lithium.common.hopper.HopperHelper.tryMoveSingleItem(to, takenItem, null); + if (transferSuccess) { + to.setChanged(); + from.setChanged(); + return true; + } + //put the item back similar to vanilla + ItemStack restoredStack = fromStackList.get(fromSlot); + if (restoredStack.isEmpty()) { + restoredStack = takenItem; + } else { + restoredStack.grow(1); + } + //calling setStack is necessary due to its side effects (markDirty in LootableContainerBlockEntity) + from.setItem(fromSlot, restoredStack); + } + } + hopperBlockEntity.myModCountAtLastExtract = hopperStackList.getModCount(); + if (fromStackList != null) { + hopperBlockEntity.extractStackListModCount = fromStackList.getModCount(); + } + return false; + } } diff --git a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java index 4d268864656ad67e6971b74534e25ac47d922cbd..a2af20103729392df0167a632672ae11caf05340 100644 --- a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java +++ b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java @@ -32,7 +32,7 @@ import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; -public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity implements WorldlyContainer { +public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity implements WorldlyContainer, net.caffeinemc.mods.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity, net.caffeinemc.mods.lithium.api.inventory.LithiumInventory { // DivineMC - lithium: sleeping_block_entity public static final int COLUMNS = 9; public static final int ROWS = 3; public static final int CONTAINER_SIZE = 27; @@ -135,6 +135,7 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl doNeighborUpdates(level, pos, state); } } + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.animationStatus == ShulkerBoxBlockEntity.AnimationStatus.CLOSED && this.progressOld == 0.0f && this.progress == 0.0f) this.lithium$startSleeping(); // DivineMC - lithium: sleeping_block_entity } public ShulkerBoxBlockEntity.AnimationStatus getAnimationStatus() { @@ -175,6 +176,7 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl @Override public boolean triggerEvent(int id, int type) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && this.sleepingTicker != null) this.wakeUpNow(); // DivineMC - lithium: sleeping_block_entity if (id == 1) { this.openCount = type; if (type == 0) { @@ -266,6 +268,7 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl @Override protected void setItems(NonNullList items) { this.itemStacks = items; + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.lithium$emitStackListReplaced(); // DivineMC - lithium: sleeping_block_entity } @Override @@ -307,4 +310,39 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl OPENED, CLOSING; } + + // DivineMC start - lithium: sleeping_block_entity + private net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + @Override + public net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { + return tickWrapper; + } + + @Override + public void lithium$setTickWrapper(net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper) { + this.tickWrapper = tickWrapper; + } + + @Override + public TickingBlockEntity lithium$getSleepingTicker() { + return sleepingTicker; + } + + @Override + public void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker) { + this.sleepingTicker = sleepingTicker; + } + + @Override + public net.minecraft.core.NonNullList getInventoryLithium() { + return itemStacks; + } + + @Override + public void setInventoryLithium(net.minecraft.core.NonNullList inventory) { + itemStacks = inventory; + } + // DivineMC end - lithium: sleeping_block_entity } diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java index a6c99a119a923dd890169888ff4e9852f6e27855..754a4c463c3706512fc42fd3ad32841fc8320709 100644 --- a/net/minecraft/world/level/block/state/BlockBehaviour.java +++ b/net/minecraft/world/level/block/state/BlockBehaviour.java @@ -84,7 +84,7 @@ import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -public abstract class BlockBehaviour implements FeatureElement { +public abstract class BlockBehaviour implements FeatureElement, net.caffeinemc.mods.lithium.common.block.entity.ShapeUpdateHandlingBlockBehaviour { // DivineMC - lithium: sleeping_block_entity protected static final Direction[] UPDATE_SHAPE_ORDER = new Direction[]{ Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH, Direction.DOWN, Direction.UP }; @@ -156,6 +156,7 @@ public abstract class BlockBehaviour implements FeatureElement { BlockState neighborState, RandomSource random ) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity) this.lithium$handleShapeUpdate(level, state, pos, neighborPos, neighborState); // DivineMC - lithium: sleeping_block_entity return state; } diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java index acddfe7fdd4305ba832af62d607a06213b29469d..d0c6cbb27237dca9028ec66f95886bcbeaf05dc2 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -933,12 +933,14 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot (pos, ticker1) -> { TickingBlockEntity tickingBlockEntity = this.createTicker(blockEntity, ticker); if (ticker1 != null) { + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && blockEntity instanceof net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity sleepingBlockEntity) sleepingBlockEntity.lithium$setTickWrapper(ticker1); // DivineMC - lithium: sleeping_block_entity ticker1.rebind(tickingBlockEntity); return (LevelChunk.RebindableTickingBlockEntityWrapper)ticker1; } else if (this.isInLevel()) { LevelChunk.RebindableTickingBlockEntityWrapper rebindableTickingBlockEntityWrapper = new LevelChunk.RebindableTickingBlockEntityWrapper( tickingBlockEntity ); + if (org.bxteam.divinemc.config.DivineConfig.PerformanceCategory.sleepingBlockEntity && blockEntity instanceof net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity sleepingBlockEntity) sleepingBlockEntity.lithium$setTickWrapper(rebindableTickingBlockEntityWrapper); // DivineMC - lithium: sleeping_block_entity this.level.addBlockEntityTicker(rebindableTickingBlockEntityWrapper); return rebindableTickingBlockEntityWrapper; } else {