From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: mrhua269 Date: Fri, 14 Nov 2025 18:20:00 +0800 Subject: [PATCH] Vanilla hopper & Leaves Lithium Sleeping Block Entity [Lithium Sleeping Block Entity] is a part from leaves Origin patch link: https://github.com/LeavesMC/Leaves/blob/master/leaves-server/minecraft-patches/features/0136-Lithium-Sleeping-Block-Entity.patch Origin license: https://github.com/LeavesMC/Leaves/blob/master/LICENSE.md diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java index 8c4d89c47fd176d584d3dbd0e3325f69b5e847c3..d0de3b22f6cef6eb213b7ff688a514a67f84fbcf 100644 --- a/io/papermc/paper/threadedregions/RegionizedWorldData.java +++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java @@ -63,6 +63,10 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; +// Luminol start - imports for lithium sleeping block entity +import org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker; // Luminol - Lithium Sleeping Block Entity +import org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionItemEntityMovementTracker; +// Luminol end public final class RegionizedWorldData { @@ -73,6 +77,32 @@ public final class RegionizedWorldData { public static final RegionizedData.RegioniserCallback REGION_CALLBACK = new RegionizedData.RegioniserCallback<>() { @Override public void merge(final RegionizedWorldData from, final RegionizedWorldData into, final long fromTickOffset) { + // Luminol start - Lithium sleeping block entity + // lithium listeners + final long fromRedstoneTimeOffsetForLithiumTracker = into.redstoneTime - from.redstoneTime; + for (var entry : from.containerEntityMovementTrackerMap.entrySet()) { + var key = entry.getKey(); + var tracker = entry.getValue(); + + if (!tracker.hasUser()) { + continue; + } + + tracker.updateTicks(fromTickOffset, fromRedstoneTimeOffsetForLithiumTracker); + into.containerEntityMovementTrackerMap.put(key, tracker); + } + for (var entry : from.itemEntityMovementTrackerMap.entrySet()) { + var key = entry.getKey(); + var tracker = entry.getValue(); + + if (!tracker.hasUser()) { + continue; + } + + tracker.updateTicks(fromTickOffset, fromRedstoneTimeOffsetForLithiumTracker); + into.itemEntityMovementTrackerMap.put(key, tracker); + } + // Luminol end // connections for (final Connection conn : from.connections) { into.connections.add(conn); @@ -111,16 +141,20 @@ public final class RegionizedWorldData { from.fluidLevelTicks.merge(into.fluidLevelTicks, fromRedstoneTimeOffset); // tile entity ticking - for (final TickingBlockEntity tileEntityWrapped : from.pendingBlockEntityTickers) { + for (TickingBlockEntity tileEntityWrapped : from.pendingBlockEntityTickers) { // Luminol - region threading for lithium sleeping block entity into.pendingBlockEntityTickers.add(tileEntityWrapped); - final BlockEntity tileEntity = tileEntityWrapped.getTileEntity(); + BlockEntity tileEntity = tileEntityWrapped.getTileEntity(); // Luminol - region threading for lithium sleeping block entity + tileEntityWrapped.updateTicksForLithium(fromRedstoneTimeOffset); // Luminol - region threading for lithium sleeping block entity + if (tileEntity == null && (tileEntityWrapped = tileEntityWrapped.getLithiumSlept()) != null) tileEntity = tileEntityWrapped.getTileEntity(); // Luminol - region threading for lithium sleeping block entity if (tileEntity != null) { tileEntity.updateTicks(fromTickOffset, fromRedstoneTimeOffset); } } - for (final TickingBlockEntity tileEntityWrapped : from.blockEntityTickers) { + for (TickingBlockEntity tileEntityWrapped : from.blockEntityTickers) { // Luminol - region threading for lithium sleeping block entity into.blockEntityTickers.add(tileEntityWrapped); - final BlockEntity tileEntity = tileEntityWrapped.getTileEntity(); + BlockEntity tileEntity = tileEntityWrapped.getTileEntity(); // Luminol - region threading for lithium sleeping block entity + tileEntityWrapped.updateTicksForLithium(fromRedstoneTimeOffset); // Luminol - region threading for lithium sleeping block entity + if (tileEntity == null && (tileEntityWrapped = tileEntityWrapped.getLithiumSlept()) != null) tileEntity = tileEntityWrapped.getTileEntity(); // Luminol - region threading for lithium sleeping block entity if (tileEntity != null) { tileEntity.updateTicks(fromTickOffset, fromRedstoneTimeOffset); } @@ -165,6 +199,37 @@ public final class RegionizedWorldData { public void split(final RegionizedWorldData from, final int chunkToRegionShift, final Long2ReferenceOpenHashMap regionToData, final ReferenceOpenHashSet dataSet) { + // Luminol start - Lithium sleeping block entity + // lithium entity movement listeners + for (var entry : from.containerEntityMovementTrackerMap.entrySet()) { + final long key = entry.getKey(); + var tracker = entry.getValue(); + + var sectionPos = net.minecraft.core.SectionPos.of(key); + var pos = sectionPos.chunk(); + + // skip no user listeners + if (!tracker.hasUser()) { + continue; + } + + regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift)).containerEntityMovementTrackerMap.put(key, tracker); + } + for (var entry : from.itemEntityMovementTrackerMap.entrySet()) { + final long key = entry.getKey(); + var tracker = entry.getValue(); + + var sectionPos = net.minecraft.core.SectionPos.of(key); + var pos = sectionPos.chunk(); + + // skip no user listeners + if (!tracker.hasUser()) { + continue; + } + + regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift)).itemEntityMovementTrackerMap.put(key, tracker); + } + // Luminol end // connections for (final Connection conn : from.connections) { final ServerPlayer player = conn.getPlayer(); @@ -240,8 +305,9 @@ public final class RegionizedWorldData { from.fluidLevelTicks.split(chunkToRegionShift, levelTicksFluidRegionData); // tile entity ticking - for (final TickingBlockEntity tileEntity : from.pendingBlockEntityTickers) { - final BlockPos pos = tileEntity.getPos(); + for (final TickingBlockEntity tileEntity : from.pendingBlockEntityTickers) { // Luminol - region threading for lithium sleeping block entity + BlockPos pos = tileEntity.getPos(); // Luminol - region threading for lithium sleeping block entity + if (pos == null) pos = tileEntity.getLithiumSlept().getPos(); // Luminol - region threading for lithium sleeping block entity final int chunkX = pos.getX() >> 4; final int chunkZ = pos.getZ() >> 4; @@ -251,8 +317,9 @@ public final class RegionizedWorldData { } // else: when a chunk unloads, it does not actually _remove_ the tile entity from the list, it just gets // marked as removed. So if there is no section, it's probably removed! } - for (final TickingBlockEntity tileEntity : from.blockEntityTickers) { - final BlockPos pos = tileEntity.getPos(); + for (final TickingBlockEntity tileEntity : from.blockEntityTickers) { // Luminol - region threading for lithium sleeping block entity + BlockPos pos = tileEntity.getPos(); // Luminol - region threading for lithium sleeping block entity + if (pos == null) pos = tileEntity.getLithiumSlept().getPos(); // Luminol - region threading for lithium sleeping block entity final int chunkX = pos.getX() >> 4; final int chunkZ = pos.getZ() >> 4; @@ -447,6 +514,11 @@ public final class RegionizedWorldData { // Redstone public final alternate.current.wire.WireHandler wireHandler; public final io.papermc.paper.redstone.RedstoneWireTurbo turbo; + // Luminol start - Lithium sleeping block entity + // Lithium + public final Map containerEntityMovementTrackerMap = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>(); + public final Map itemEntityMovementTrackerMap = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>(); + // Luminol end public RegionizedWorldData(final ServerLevel world) { this.world = world; diff --git a/net/minecraft/core/NonNullList.java b/net/minecraft/core/NonNullList.java index 7e31c5c8659d24948fd45a2d6ee7bdeca6027d27..387325b971b7cec6aeb677bf6c49b8bf21b3a344 100644 --- a/net/minecraft/core/NonNullList.java +++ b/net/minecraft/core/NonNullList.java @@ -9,7 +9,7 @@ import javax.annotation.Nullable; import org.apache.commons.lang3.Validate; public class NonNullList extends AbstractList { - private final List list; + public final List list; // Leaves - private -> public @Nullable private final E defaultValue; diff --git a/net/minecraft/core/component/PatchedDataComponentMap.java b/net/minecraft/core/component/PatchedDataComponentMap.java index 3af6c1e2549ba3aeb60aa9d498a976be3680c0ee..262a575d17e95a0fbff4ecac4a4813ae23b83cf3 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, org.leavesmc.leaves.lithium.common.util.change_tracking.ChangePublisher { // Leaves - Lithium Sleeping Block Entity private final DataComponentMap prototype; private Reference2ObjectMap, Optional> patch; private boolean copyOnWrite; @@ -135,6 +135,7 @@ public final class PatchedDataComponentMap implements DataComponentMap { } private void ensureMapOwnership() { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.subscriber != null) this.subscriber.lithium$notify((PatchedDataComponentMap) (Object) this, 0); // Leaves - Lithium Sleeping Block Entity if (this.copyOnWrite) { this.patch = new Reference2ObjectArrayMap<>(this.patch); this.copyOnWrite = false; @@ -238,4 +239,22 @@ public final class PatchedDataComponentMap implements DataComponentMap { public String toString() { return "{" + this.stream().map(TypedDataComponent::toString).collect(Collectors.joining(", ")) + "}"; } + + // Leaves start - Lithium Sleeping Block Entity + private org.leavesmc.leaves.lithium.common.util.change_tracking.ChangeSubscriber subscriber; + + @Override + public void lithium$subscribe(org.leavesmc.leaves.lithium.common.util.change_tracking.ChangeSubscriber subscriber, int subscriberData) { + if (subscriberData != 0) { + throw new UnsupportedOperationException("ComponentMapImpl does not support subscriber data"); + } + this.subscriber = org.leavesmc.leaves.lithium.common.util.change_tracking.ChangeSubscriber.combine(this.subscriber, 0, subscriber, 0); + } + + @Override + public int lithium$unsubscribe(org.leavesmc.leaves.lithium.common.util.change_tracking.ChangeSubscriber subscriber) { + this.subscriber = org.leavesmc.leaves.lithium.common.util.change_tracking.ChangeSubscriber.without(this.subscriber, subscriber); + return 0; + } + // Leaves end - Lithium Sleeping Block Entity } diff --git a/net/minecraft/server/commands/data/EntityDataAccessor.java b/net/minecraft/server/commands/data/EntityDataAccessor.java index 3092454bf7071deca75fecfc203072593fe5c7e7..90e888321b6e208163742c3f1f836433f4d23cbc 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 (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.entity instanceof net.minecraft.world.entity.item.ItemEntity itemEntity) itemEntity.levelCallback.onMove(); // Leaves - Lithium Sleeping Block Entity } } } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java index 64b221fd15b5c26621a412dc152647908da850ec..7584ba257d61bf650d83a2316ff012df928d54f3 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -2533,6 +2533,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe for (TickingBlockEntity tickingBlockEntity : (Iterable)null) { // Folia - region threading BlockPos pos = tickingBlockEntity.getPos(); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && pos == null) pos = BlockPos.ZERO; // Leaves - 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 b382665cc125b8b5c0938e5e55984e4bf91d37ff..2afaf6a3f18af7889f4f3b35cee9bbb5e4ec01f2 100644 --- a/net/minecraft/world/Container.java +++ b/net/minecraft/world/Container.java @@ -11,7 +11,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, org.leavesmc.leaves.lithium.api.inventory.LithiumCooldownReceivingInventory, org.leavesmc.leaves.lithium.api.inventory.LithiumTransferConditionInventory { // Leaves - 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 66a55e21731bc64fd3446a3706fba62c13bb4b10..38092dcc3dedc6c855e5598126be47f892223547 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java @@ -308,7 +308,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess private static final EntityDataAccessor DATA_NO_GRAVITY = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.BOOLEAN); protected static final EntityDataAccessor DATA_POSE = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.POSE); public static final EntityDataAccessor DATA_TICKS_FROZEN = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.INT); - private EntityInLevelCallback levelCallback = EntityInLevelCallback.NULL; + public EntityInLevelCallback levelCallback = EntityInLevelCallback.NULL; // Leaves - private -> public private final VecDeltaCodec packetPositionCodec = new VecDeltaCodec(); public boolean hasImpulse; @Nullable @@ -4309,11 +4309,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } protected Entity transformForAsyncTeleport(ServerLevel destination, Vec3 pos, Float yaw, Float pitch, Vec3 velocity) { + final boolean toSameWorld = this.level != destination; // Luminol - region threading for sleeping block entity this.removeAfterChangingDimensions(); // remove before so that any CBEntity#getHandle call affects this entity before copying Entity copy = this.getType().create(destination, EntitySpawnReason.DIMENSION_TRAVEL); copy.restoreFrom(this); copy.transform(pos, yaw, pitch, velocity); + if (toSameWorld) copy.notifyLithiumTrackerIfNeeded();// Luminol - region threading for sleeping block entity // vanilla code used to call remove _after_ copying, and some stuff is required to be after copy - so add hook here // for example, clearing of inventory after switching dimensions this.postRemoveAfterChangingDimensions(); @@ -6077,6 +6079,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.setBoundingBox(this.makeBoundingBox()); } // Paper end - Block invalid positions and bounding box + this.notifyLithiumTrackerIfNeeded(); // Luminol - Leaves lithium sleeping block entity + } + // Luminol start - split out sleeping block entity notify method from leaves's implementation + private void notifyLithiumTrackerIfNeeded() { + // Leaves start - Lithium Sleeping Block Entity + if (!me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) return; + var currentWorldData = io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentRegionizedWorldData(); + if (currentWorldData == null || currentWorldData.world != this.level) return; + if (this instanceof ItemEntity) { + long sectionKey = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionKey(this); + org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionItemEntityMovementTracker tracker = currentWorldData.itemEntityMovementTrackerMap.get(sectionKey); + if (tracker != null) tracker.notifyAllListeners(currentWorldData.getRedstoneGameTime()); // Luminol - region threading for sleeping block entity + } + else if (this instanceof net.minecraft.world.entity.vehicle.ContainerEntity) { + long sectionKey = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionKey(this); + org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker tracker = currentWorldData.containerEntityMovementTrackerMap.get(sectionKey); + if (tracker != null) tracker.notifyAllListeners(currentWorldData.getRedstoneGameTime()); // Luminol - region threading for sleeping block entity + } + // Leaves 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 b745eb5d9c68547335247910ff2ae8d5fb36349c..fd4f1e63f980b6959fc80084651991f8f8bc6c34 100644 --- a/net/minecraft/world/entity/item/ItemEntity.java +++ b/net/minecraft/world/entity/item/ItemEntity.java @@ -34,8 +34,12 @@ import net.minecraft.world.level.portal.TeleportTransition; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.Vec3; +// Leaves start - Lithium Sleeping Block Entity +import org.leavesmc.leaves.lithium.common.util.change_tracking.ChangePublisher; +import org.leavesmc.leaves.lithium.common.util.change_tracking.ChangeSubscriber; +// Leaves end - Lithium Sleeping Block Entity -public class ItemEntity extends Entity implements TraceableEntity { +public class ItemEntity extends Entity implements TraceableEntity, ChangePublisher, ChangeSubscriber.CountChangeSubscriber { // Leaves - 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; @@ -527,6 +531,25 @@ public class ItemEntity extends Entity implements TraceableEntity { } public void setItem(ItemStack stack) { + // Leaves start - Lithium Sleeping Block Entity + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && 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; + } + } + } + // Leaves 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 } @@ -606,4 +629,76 @@ public class ItemEntity extends Entity implements TraceableEntity { public SlotAccess getSlot(int slot) { return slot == 0 ? SlotAccess.of(this::getItem, this::setItem) : super.getSlot(slot); } + + // Leaves start - Lithium Sleeping Block Entity + private ChangeSubscriber subscriber; + //Stores the data of the subscriber, unless the subscriber is a Multi which stores the data in a list, in which case this variable stores 0 + private int subscriberData; + + private void startTrackingChanges() { + ItemStack stack = this.getItem(); + if (!stack.isEmpty()) { + stack.lithium$subscribe(this, 0); + } + } + + @Override + public void lithium$subscribe(ChangeSubscriber subscriber, int subscriberData) { + if (this.subscriber == null) { + this.startTrackingChanges(); + } + this.subscriber = ChangeSubscriber.combine(this.subscriber, this.subscriberData, subscriber, subscriberData); + if (this.subscriber instanceof ChangeSubscriber.Multi) { + this.subscriberData = 0; + } else { + this.subscriberData = subscriberData; + } + } + + @Override + public int lithium$unsubscribe(ChangeSubscriber subscriber) { + int retval = ChangeSubscriber.dataOf(this.subscriber, subscriber, this.subscriberData); + this.subscriberData = ChangeSubscriber.dataWithout(this.subscriber, subscriber, this.subscriberData); + this.subscriber = 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 ChangeSubscriber.CountChangeSubscriber countChangeSubscriber) { + countChangeSubscriber.lithium$notifyCount(this, this.subscriberData, newCount); + } + } + // Leaves end - Lithium Sleeping Block Entity } diff --git a/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java index 6a008c86f4e360c916b93f0e3a62a9d8b43e74e6..4e4434334e979bd8bf61bc81cca43a9a1304ea06 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, org.leavesmc.leaves.lithium.api.inventory.LithiumInventory { // Leaves - Lithium Sleeping Block Entity private NonNullList itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 @Nullable public ResourceKey lootTable; @@ -218,4 +218,15 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme return this.getBukkitEntity().getLocation(); } // CraftBukkit end + // Leaves 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; + } + // Leaves end - Lithium Sleeping Block Entity } diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java index 39a6e3eb7e515060cd35c3d78ff7ba74e159588d..2d6acceffb2cb7e55dd504018a70b05afccd9c11 100644 --- a/net/minecraft/world/inventory/AbstractContainerMenu.java +++ b/net/minecraft/world/inventory/AbstractContainerMenu.java @@ -898,6 +898,7 @@ public abstract class AbstractContainerMenu { } else { float f = 0.0F; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && container instanceof org.leavesmc.leaves.lithium.api.inventory.LithiumInventory optimizedInventory) return org.leavesmc.leaves.lithium.common.hopper.InventoryHelper.getLithiumStackList(optimizedInventory).getSignalStrength(container); // Leaves - 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 6154da1158756cff072d7e8cae10e20eafed1eec..33c3a5e39e8e1aa023baab76ef94f8aed481480e 100644 --- a/net/minecraft/world/item/ItemStack.java +++ b/net/minecraft/world/item/ItemStack.java @@ -93,8 +93,12 @@ import net.minecraft.world.level.block.state.pattern.BlockInWorld; import org.apache.commons.lang3.function.TriConsumer; import org.apache.commons.lang3.mutable.MutableBoolean; import org.slf4j.Logger; +// Leaves start - Lithium Sleeping Block Entity +import org.leavesmc.leaves.lithium.common.util.change_tracking.ChangePublisher; +import org.leavesmc.leaves.lithium.common.util.change_tracking.ChangeSubscriber; +// Leaves end - Lithium Sleeping Block Entity -public final class ItemStack implements DataComponentHolder { +public final class ItemStack implements DataComponentHolder, ChangePublisher, ChangeSubscriber { // Leaves - 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), @@ -951,6 +955,7 @@ public final class ItemStack implements DataComponentHolder { @Nullable public T set(DataComponentType component, @Nullable T value) { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && component == DataComponents.ENCHANTMENTS && this.subscriber instanceof ChangeSubscriber.EnchantmentSubscriber enchantmentSubscriber) enchantmentSubscriber.lithium$notifyAfterEnchantmentChange(this, this.subscriberData); // Leaves - Lithium Sleeping Block Entity return this.components.set(component, value); } @@ -1295,6 +1300,23 @@ public final class ItemStack implements DataComponentHolder { } public void setCount(int count) { + // Leaves start - Lithium Sleeping Block Entity + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && count != this.count) { + if (this.subscriber instanceof 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; + } + } + } + // Leaves end - Lithium Sleeping Block Entity this.count = count; } @@ -1350,4 +1372,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); } + + // Leaves start - Lithium Sleeping Block Entity + private ChangeSubscriber subscriber; + private int subscriberData; + + @Override + public void lithium$subscribe(ChangeSubscriber subscriber, int subscriberData) { + if (this.isEmpty()) { + throw new IllegalStateException("Cannot subscribe to an empty ItemStack!"); + } + + if (this.subscriber == null) { + this.startTrackingChanges(); + } + this.subscriber = ChangeSubscriber.combine(this.subscriber, this.subscriberData, subscriber, subscriberData); + if (this.subscriber instanceof ChangeSubscriber.Multi) { + this.subscriberData = 0; + } else { + this.subscriberData = subscriberData; + } + } + + @Override + public int lithium$unsubscribe(ChangeSubscriber subscriber) { + if (this.isEmpty()) { + throw new IllegalStateException("Cannot unsubscribe from an empty ItemStack!"); + } + + int retval = ChangeSubscriber.dataOf(this.subscriber, subscriber, this.subscriberData); + this.subscriberData = ChangeSubscriber.dataWithout(this.subscriber, subscriber, this.subscriberData); + this.subscriber = ChangeSubscriber.without(this.subscriber, subscriber); + + if (this.subscriber == null) { + this.components.lithium$unsubscribe(this); + } + return retval; + } + + @Override + public void lithium$unsubscribeWithData(ChangeSubscriber subscriber, int subscriberData) { + if (this.isEmpty()) { + throw new IllegalStateException("Cannot unsubscribe from an empty ItemStack!"); + } + + this.subscriberData = ChangeSubscriber.dataWithout(this.subscriber, subscriber, this.subscriberData, subscriberData, true); + this.subscriber = ChangeSubscriber.without(this.subscriber, subscriber, subscriberData, true); + + if (this.subscriber == null) { + this.components.lithium$unsubscribe(this); + } + } + + @Override + public boolean lithium$isSubscribedWithData(ChangeSubscriber subscriber, int subscriberData) { + if (this.isEmpty()) { + throw new IllegalStateException("Cannot be subscribed to an empty ItemStack!"); + } + + return 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); + } + } + // Leaves end - Lithium Sleeping Block Entity } diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java index b2aee3194b71b5f2da71c1260b18fd87d0bdc825..4eb08dc3b3d7ed8716d7e37313512c8bc5e4e393 100644 --- a/net/minecraft/world/level/Level.java +++ b/net/minecraft/world/level/Level.java @@ -1513,7 +1513,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl // Spigot end if (tickingBlockEntity.isRemoved()) { toRemove.add(tickingBlockEntity); // Paper - Fix MC-117075; use removeAll - } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { + } else if (runsNormally && this.shouldTickBlockPosFilterNull(tickingBlockEntity.getPos())) { // Leaves - Lithium Sleeping Block Entity tickingBlockEntity.tick(); // Paper start - rewrite chunk system if ((++tickedEntities & 7) == 0) { @@ -2205,4 +2205,25 @@ public abstract class Level implements LevelAccessor, UUIDLookup, AutoCl return this.id; } } + + // Leaves start - Lithium Sleeping Block Entity + public BlockEntity lithium$getLoadedExistingBlockEntity(BlockPos pos) { + if (!this.isOutsideBuildHeight(pos)) { + if (this.isClientSide || ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this, pos)) { // Luminol - region threading for sleeping block entity + 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); + } + // Leaves end - Lithium Sleeping Block Entity } diff --git a/net/minecraft/world/level/block/ComposterBlock.java b/net/minecraft/world/level/block/ComposterBlock.java index a647d76d365a60b95a3eb7927ac426bf70d417f3..f7f2fa0db2ffa6a6e5d076159c5b5b6f3ac6797d 100644 --- a/net/minecraft/world/level/block/ComposterBlock.java +++ b/net/minecraft/world/level/block/ComposterBlock.java @@ -411,7 +411,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { } } - public static class EmptyContainer extends SimpleContainer implements WorldlyContainer { + public static class EmptyContainer extends SimpleContainer implements WorldlyContainer, org.leavesmc.leaves.lithium.common.hopper.BlockStateOnlyInventory { // Leaves - 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 @@ -433,7 +433,7 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { } } - public static class InputContainer extends SimpleContainer implements WorldlyContainer { + public static class InputContainer extends SimpleContainer implements WorldlyContainer, org.leavesmc.leaves.lithium.common.hopper.BlockStateOnlyInventory { // Leaves - Lithium Sleeping Block Entity private final BlockState state; private final LevelAccessor level; private final BlockPos pos; @@ -479,12 +479,13 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder { } // Paper end - Add CompostItemEvent and EntityCompostItemEvent this.level.levelEvent(1500, this.pos, blockState != this.state ? 1 : 0); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.changed = false; // Leaves - Lithium Sleeping Block Entity this.removeItemNoUpdate(0); } } } - public static class OutputContainer extends SimpleContainer implements WorldlyContainer { + public static class OutputContainer extends SimpleContainer implements WorldlyContainer, org.leavesmc.leaves.lithium.common.hopper.BlockStateOnlyInventory { // Leaves - 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 558751ade918a92a1173096ccfeacf238f4260d0..0ab376b4207b695e92c324b8a5f9818d3f4accde 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 isMoving) { this.updateNeighborsInFront(level, pos, state); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this instanceof ComparatorBlock && !oldState.is(Blocks.COMPARATOR)) org.leavesmc.leaves.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracking.notifyNearbyBlockEntitiesAboutNewComparator(level, pos); // Leaves - Lithium Sleeping Block Entity } @Override diff --git a/net/minecraft/world/level/block/HopperBlock.java b/net/minecraft/world/level/block/HopperBlock.java index 46a27f60ba407dacdac190b5e292ab3f1db5a078..912d1ba98809b4469e1468e4855bb1bc91bb4540 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 org.leavesmc.leaves.lithium.common.block.entity.ShapeUpdateHandlingBlockBehaviour { // Leaves - 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,17 @@ public class HopperBlock extends BaseEntityBlock { protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) { if (!oldState.is(state.getBlock())) { this.checkPoweredState(level, pos, state); + // Leaves start - Lithium Sleeping Block Entity + //invalidate caches of nearby hoppers when placing an update suppressed hopper + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && level.getBlockState(pos) != state) { + for (Direction direction : UPDATE_SHAPE_ORDER) { + BlockEntity hopper = level.lithium$getLoadedExistingBlockEntity(pos.relative(direction)); + if (hopper instanceof org.leavesmc.leaves.lithium.common.hopper.UpdateReceiver updateReceiver) { + updateReceiver.lithium$invalidateCacheOnNeighborUpdate(direction == Direction.DOWN); + } + } + } + // Leaves end - Lithium Sleeping Block Entity } } @@ -115,6 +126,7 @@ public class HopperBlock extends BaseEntityBlock { @Override protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && level.lithium$getLoadedExistingBlockEntity(pos) instanceof org.leavesmc.leaves.lithium.common.hopper.UpdateReceiver updateReceiver) updateReceiver.lithium$invalidateCacheOnUndirectedNeighborUpdate(); // Leaves - Lithium Sleeping Block Entity /* invalidate cache when the block is replaced */ this.checkPoweredState(level, pos, state); } @@ -168,4 +180,25 @@ public class HopperBlock extends BaseEntityBlock { protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) { return false; } + + // Leaves start - Lithium Sleeping Block Entity + @Override + public void lithium$handleShapeUpdate(net.minecraft.world.level.LevelReader levelReader, BlockState myBlockState, BlockPos myPos, BlockPos posFrom, BlockState newState) { + //invalidate cache when composters change state + 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 org.leavesmc.leaves.lithium.common.hopper.UpdateReceiver updateReceiver) { + updateReceiver.lithium$invalidateCacheOnNeighborUpdate(above); + } + } + } + // Leaves 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 36a72a11d28f99bfe85868461925b778cc01478e..d7a211220d5eba7e34b9083f06f39e9527962599 100644 --- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java @@ -39,7 +39,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, org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity, org.leavesmc.leaves.lithium.common.block.entity.SetChangedHandlingBlockEntity, org.leavesmc.leaves.lithium.api.inventory.LithiumInventory { // Leaves - 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; @@ -164,6 +164,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 (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.isSleeping() && this.level != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity } @Override @@ -269,6 +270,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit if (flag) { setChanged(level, pos, state); } + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) furnace.checkSleep(state); // Leaves - Lithium Sleeping Block Entity } private static boolean canBurn( @@ -527,4 +529,53 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit this.getRecipesToAwardAndPopExperience(serverLevel, Vec3.atCenterOf(pos)); } } + + // Leaves 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; + } + // Leaves 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 d679ab599dfd0bdbdc3ab5530d7fcd1c38baf7fa..de99652629c7301a4bc0e9ec48bed9a26a338f0d 100644 --- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java @@ -20,7 +20,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 org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, org.leavesmc.leaves.lithium.api.inventory.LithiumInventory { // Leaves - Lithium Sleeping Block Entity // CraftBukkit start - add fields and methods public java.util.List transaction = new java.util.ArrayList<>(); private int maxStack = MAX_STACK; @@ -119,6 +119,7 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity { @Override protected void setItems(NonNullList items) { this.items = items; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity } @Override @@ -162,4 +163,18 @@ 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); } + + // Leaves 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; + } + // Leaves 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 5a094257a31f0500278a706a418e1697f8810ffb..10b2cc20bbf3b70e4e09dbbe14b90506e6366158 100644 --- a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java @@ -23,8 +23,17 @@ import net.minecraft.world.item.component.ItemContainerContents; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; +// Leaves start - Lithium Sleeping Block Entity +import it.unimi.dsi.fastutil.objects.ReferenceArraySet; +import org.leavesmc.leaves.lithium.api.inventory.LithiumInventory; +import org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeEmitter; +import org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener; +import org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker; +import org.leavesmc.leaves.lithium.common.hopper.InventoryHelper; +import org.leavesmc.leaves.lithium.common.hopper.LithiumStackList; +// Leaves end - Lithium Sleeping Block Entity -public abstract class BaseContainerBlockEntity extends BlockEntity implements Container, MenuProvider, Nameable { +public abstract class BaseContainerBlockEntity extends BlockEntity implements Container, MenuProvider, Nameable, InventoryChangeEmitter { public LockCode lockKey = LockCode.NO_LOCK; @Nullable public Component name; @@ -38,6 +47,7 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co super.loadAdditional(input); this.lockKey = LockCode.fromTag(input); this.name = parseCustomNameSafe(input, "CustomName"); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this instanceof InventoryChangeTracker inventoryChangeTracker) inventoryChangeTracker.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity } @Override @@ -200,4 +210,97 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co return org.bukkit.craftbukkit.util.CraftLocation.toBukkit(this.worldPosition, this.level); } // CraftBukkit end + + // Leaves start - Lithium Sleeping Block Entity + ReferenceArraySet inventoryChangeListeners = null; + ReferenceArraySet inventoryHandlingTypeListeners = null; + + @Override + public void lithium$emitContentModified() { + ReferenceArraySet inventoryChangeListeners = this.inventoryChangeListeners; + if (inventoryChangeListeners != null) { + for (InventoryChangeListener inventoryChangeListener : inventoryChangeListeners) { + inventoryChangeListener.lithium$handleInventoryContentModified(this); + } + inventoryChangeListeners.clear(); + } + } + + @Override + public void lithium$emitStackListReplaced() { + ReferenceArraySet listeners = this.inventoryHandlingTypeListeners; + if (listeners != null && !listeners.isEmpty()) { + for (InventoryChangeListener inventoryChangeListener : listeners) { + inventoryChangeListener.handleStackListReplaced(this); + } + listeners.clear(); + } + + if (this instanceof InventoryChangeListener listener) { + listener.handleStackListReplaced(this); + } + + this.invalidateChangeListening(); + } + + @Override + public void lithium$emitRemoved() { + ReferenceArraySet listeners = this.inventoryHandlingTypeListeners; + if (listeners != null && !listeners.isEmpty()) { + for (InventoryChangeListener listener : listeners) { + listener.lithium$handleInventoryRemoved(this); + } + listeners.clear(); + } + + if (this instanceof InventoryChangeListener listener) { + listener.lithium$handleInventoryRemoved(this); + } + + this.invalidateChangeListening(); + } + + private void invalidateChangeListening() { + if (this.inventoryChangeListeners != null) { + this.inventoryChangeListeners.clear(); + } + + LithiumStackList lithiumStackList = this instanceof LithiumInventory ? InventoryHelper.getLithiumStackListOrNull((LithiumInventory) this) : null; + if (lithiumStackList != null && this instanceof InventoryChangeTracker inventoryChangeTracker) { + lithiumStackList.removeInventoryModificationCallback(inventoryChangeTracker); + } + } + + @Override + public void lithium$emitFirstComparatorAdded() { + ReferenceArraySet inventoryChangeListeners = this.inventoryChangeListeners; + if (inventoryChangeListeners != null && !inventoryChangeListeners.isEmpty()) { + inventoryChangeListeners.removeIf(inventoryChangeListener -> inventoryChangeListener.lithium$handleComparatorAdded(this)); + } + } + + @Override + public void lithium$forwardContentChangeOnce(InventoryChangeListener inventoryChangeListener, LithiumStackList stackList, InventoryChangeTracker thisTracker) { + if (this.inventoryChangeListeners == null) { + this.inventoryChangeListeners = new ReferenceArraySet<>(1); + } + stackList.setInventoryModificationCallback(thisTracker); + this.inventoryChangeListeners.add(inventoryChangeListener); + + } + + @Override + public void lithium$forwardMajorInventoryChanges(InventoryChangeListener inventoryChangeListener) { + if (this.inventoryHandlingTypeListeners == null) { + this.inventoryHandlingTypeListeners = new ReferenceArraySet<>(1); + } + this.inventoryHandlingTypeListeners.add(inventoryChangeListener); + } + + @Override + public void lithium$stopForwardingMajorInventoryChanges(InventoryChangeListener inventoryChangeListener) { + if (this.inventoryHandlingTypeListeners != null) { + this.inventoryHandlingTypeListeners.remove(inventoryChangeListener); + } + } } diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java index 63ac5759a8a4b59297d5eb1abd362fb2a54353d7..4c5d8e02d92e805cea922ee9ab724b785a67e538 100644 --- a/net/minecraft/world/level/block/entity/BlockEntity.java +++ b/net/minecraft/world/level/block/entity/BlockEntity.java @@ -33,9 +33,17 @@ import net.minecraft.world.level.storage.TagValueOutput; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import org.slf4j.Logger; - -public abstract class BlockEntity { - static final ThreadLocal IGNORE_TILE_UPDATES = ThreadLocal.withInitial(() -> Boolean.FALSE); // Paper - Perf: Optimize Hoppers // Folia - region threading +// Leaves start - Lithium Sleeping Block Entity +import net.minecraft.core.Direction; +import org.leavesmc.leaves.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracker; +import org.leavesmc.leaves.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracking; +import org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker; +import org.leavesmc.leaves.lithium.common.block.entity.SetBlockStateHandlingBlockEntity; +import org.leavesmc.leaves.lithium.common.block.entity.SetChangedHandlingBlockEntity; +// Leaves end - Lithium Sleeping Block Entity + +public abstract class BlockEntity implements ComparatorTracker, SetBlockStateHandlingBlockEntity, SetChangedHandlingBlockEntity { // Leaves - Lithium Sleeping Block Entity + //static final ThreadLocal IGNORE_TILE_UPDATES = ThreadLocal.withInitial(() -> Boolean.FALSE); // Paper - Perf: Optimize Hoppers // Folia - region threading // Luminol - vanilla hopper // CraftBukkit start - data containers private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); public final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer persistentDataContainer; @@ -62,6 +70,7 @@ public abstract class BlockEntity { this.validateBlockState(blockState); this.blockState = blockState; this.persistentDataContainer = new org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer(DATA_TYPE_REGISTRY); // Paper - always init + this.hasComparators = UNKNOWN; // Leaves - Lithium Sleeping Block Entity } private void validateBlockState(BlockState state) { @@ -234,8 +243,9 @@ public abstract class BlockEntity { public void setChanged() { if (this.level != null) { - if (IGNORE_TILE_UPDATES.get().booleanValue()) return; // Paper - Perf: Optimize Hoppers // Folia - region threading + //if (IGNORE_TILE_UPDATES.get().booleanValue()) return; // Paper - Perf: Optimize Hoppers // Folia - region threading // Luminol - vanilla hopper setChanged(this.level, this.worldPosition, this.blockState); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) lithium$handleSetChanged(); // Leaves - Lithium Sleeping Block Entity } } @@ -268,7 +278,9 @@ public abstract class BlockEntity { } public void setRemoved() { + this.hasComparators = UNKNOWN; // Leaves - Lithium Sleeping Block Entity this.remove = true; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.level != null && !this.level.isClientSide() && this instanceof InventoryChangeTracker inventoryChangeTracker) inventoryChangeTracker.lithium$emitRemoved(); // Leaves - Lithium Sleeping Block Entity } public void clearRemoved() { @@ -308,6 +320,7 @@ public abstract class BlockEntity { public void setBlockState(BlockState blockState) { this.validateBlockState(blockState); this.blockState = blockState; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$handleSetBlockState(); // Leaves - Lithium Sleeping Block Entity } protected void applyImplicitComponents(DataComponentGetter componentGetter) { @@ -408,4 +421,32 @@ public abstract class BlockEntity { return this.blockEntity.getNameForReporting() + "@" + this.blockEntity.getBlockPos(); } } + + // Leaves 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(Direction direction, int offset) { + byte hasComparators = this.hasComparators; + if (direction.getAxis() != Direction.Axis.Y && hasComparators != COMPARATOR_PRESENT && offset >= 1 && offset <= 2) { + this.hasComparators = COMPARATOR_PRESENT; + + if (this instanceof InventoryChangeTracker inventoryChangeTracker) { + inventoryChangeTracker.lithium$emitFirstComparatorAdded(); + } + } + } + + @Override + public boolean lithium$hasAnyComparatorNearby() { + if (this.hasComparators == UNKNOWN) { + this.hasComparators = ComparatorTracking.findNearbyComparators(this.level, this.worldPosition) ? COMPARATOR_PRESENT : COMPARATOR_ABSENT; + } + return this.hasComparators == COMPARATOR_PRESENT; + } + // Leaves 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 79a9f1c87de30cda479b55cf70fbc3219a3dcad4..6a646b768b7c0b2eaee65424fb59900ca24d6f24 100644 --- a/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java +++ b/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.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 class BrewingStandBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer { +public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity, org.leavesmc.leaves.lithium.common.block.entity.SetChangedHandlingBlockEntity, org.leavesmc.leaves.lithium.api.inventory.LithiumInventory { // Leaves - 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}; @@ -135,6 +135,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements } public static void serverTick(Level level, BlockPos pos, BlockState state, BrewingStandBlockEntity blockEntity) { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) blockEntity.checkSleep(state); // Leaves - Lithium Sleeping Block Entity ItemStack itemStack = blockEntity.items.get(4); if (blockEntity.fuel <= 0 && itemStack.is(ItemTags.BREWING_FUEL)) { // CraftBukkit start @@ -152,6 +153,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements itemStack.shrink(1); } // CraftBukkit end + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) blockEntity.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity setChanged(level, pos, state); } @@ -166,7 +168,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements } else if (!isBrewable || !itemStack1.is(blockEntity.ingredient)) { blockEntity.brewTime = 0; } - + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) blockEntity.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity setChanged(level, pos, state); } else if (isBrewable && blockEntity.fuel > 0) { blockEntity.fuel--; @@ -179,6 +181,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 (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) blockEntity.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity setChanged(level, pos, state); } @@ -285,6 +288,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements } this.fuel = input.getByteOr("Fuel", (byte)0); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.isSleeping() && this.level != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity } @Override @@ -331,4 +335,52 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements protected AbstractContainerMenu createMenu(int id, Inventory player) { return new BrewingStandMenu(id, player, this, this.dataAccess); } + // Leaves 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; + } + // Leaves 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 fb7932e17d7d00ee3050e71c88510fa23befb1bb..1773e7f3a85c8c5c550ac3f53b5abf783ee2e500 100644 --- a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java +++ b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java @@ -38,7 +38,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, org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity { // Leaves - Lithium Sleeping Block Entity private static final Logger LOGGER = LogUtils.getLogger(); private static final int BURN_COOL_SPEED = 2; private static final int NUM_SLOTS = 4; @@ -112,7 +112,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { if (flag) { setChanged(level, pos, state); - } + } else if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) campfire.lithium$startSleeping(); // Leaves - Lithium Sleeping Block Entity } public static void cooldownTick(Level level, BlockPos pos, BlockState state, CampfireBlockEntity blockEntity) { @@ -127,7 +127,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { if (flag) { setChanged(level, pos, state); - } + } else if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) blockEntity.lithium$startSleeping(); // Leaves - Lithium Sleeping Block Entity } public static void particleTick(Level level, BlockPos pos, BlockState state, CampfireBlockEntity blockEntity) { @@ -183,6 +183,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 (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity } @Override @@ -237,6 +238,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { this.cookingTime[i] = event.getTotalCookTime(); // i -> event.getTotalCookTime() // CraftBukkit end this.cookingProgress[i] = 0; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.wakeUpNow(); // Leaves - 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(); @@ -280,4 +282,30 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { public void removeComponentsFromTag(ValueOutput output) { output.discard("Items"); } + + // Leaves 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; + } + // Leaves 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 b7d94ebe0ee995392c355c4237da8443dcc79b21..68cd7f470933b3d4a03fa6bcd0e9b9fd7b49f63e 100644 --- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java +++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java @@ -24,7 +24,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, org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeEmitter, org.leavesmc.leaves.lithium.common.block.entity.SetBlockStateHandlingBlockEntity, org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity, org.leavesmc.leaves.lithium.api.inventory.LithiumInventory { // Leaves - Lithium Sleeping Block Entity private static final int EVENT_SET_OPEN_COUNT = 1; private NonNullList items = NonNullList.withSize(27, ItemStack.EMPTY); public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() { @@ -127,6 +127,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement public static void lidAnimateTick(Level level, BlockPos pos, BlockState state, ChestBlockEntity blockEntity) { blockEntity.chestLidController.tickLid(); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) blockEntity.checkSleep(); // Leaves - Lithium Sleeping Block Entity } public static void playSound(Level level, BlockPos pos, BlockState state, SoundEvent sound) { @@ -148,6 +149,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement @Override public boolean triggerEvent(int id, int type) { if (id == 1) { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.sleepingTicker != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity this.chestLidController.shouldBeOpen(type > 0); return true; } else { @@ -177,6 +179,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement @Override protected void setItems(NonNullList items) { this.items = items; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity } @Override @@ -217,4 +220,52 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement Block block = state.getBlock(); level.blockEvent(pos, block, 1, eventParam); } + + // Leaves start - Lithium Sleeping Block Entity + private net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + private TickingBlockEntity sleepingTicker = null; + + private void checkSleep() { + //If the animation is finished, it will stay unchanged until the next triggerEvent, which may change shouldBeOpen + 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; + } + // Leaves 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 969ac280ae563e3412dba406ba68ceaa8a75d519..b7e804676c39a26174758c9491f2ef4209b51f2e 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 Container { +public class ChiseledBookShelfBlockEntity extends BlockEntity implements Container, org.leavesmc.leaves.lithium.api.inventory.LithiumTransferConditionInventory { // Leaves - 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; @@ -195,4 +195,6 @@ public class ChiseledBookShelfBlockEntity extends BlockEntity implements Contain public void removeComponentsFromTag(ValueOutput output) { output.discard("Items"); } + + @Override public boolean lithium$itemInsertionTestRequiresStackSize1() {return true;} // Leaves - 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 9ce4b5a3954eda08ef587cf95dec8ed119b7a598..468730ad46c30d02f34b2abfe5a9092b507df7be 100644 --- a/net/minecraft/world/level/block/entity/CrafterBlockEntity.java +++ b/net/minecraft/world/level/block/entity/CrafterBlockEntity.java @@ -22,7 +22,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, org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity, org.leavesmc.leaves.lithium.common.block.entity.SetChangedHandlingBlockEntity { public static final int CONTAINER_WIDTH = 3; public static final int CONTAINER_HEIGHT = 3; public static final int CONTAINER_SIZE = 9; @@ -169,6 +169,7 @@ public class CrafterBlockEntity extends RandomizableContainerBlockEntity impleme } }); this.containerData.set(9, input.getIntOr("triggered", 0)); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.isSleeping() && this.level != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity } @Override @@ -278,10 +279,12 @@ public class CrafterBlockEntity extends RandomizableContainerBlockEntity impleme level.setBlock(pos, state.setValue(CrafterBlock.CRAFTING, false), 3); } } + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && i < 0) crafter.checkSleep(); // Leaves - Lithium Sleeping Block Entity } public void setCraftingTicksRemaining(int craftingTicksRemaining) { this.craftingTicksRemaining = craftingTicksRemaining; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.isSleeping() && this.level != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity } public int getRedstoneSignal() { @@ -300,4 +303,43 @@ public class CrafterBlockEntity extends RandomizableContainerBlockEntity impleme private boolean slotCanBeDisabled(int slot) { return slot > -1 && slot < 9 && this.items.get(slot).isEmpty(); } + + // Leaves 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(); + } + } + // Leaves 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 ae52dc75335799e55e403e3d3f11e9f1d67e4305..124b13fac1e7c6677a729925de20f17c67757812 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 org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, org.leavesmc.leaves.lithium.api.inventory.LithiumInventory { // Leaves - Lithium Sleeping Block Entity public static final int CONTAINER_SIZE = 9; private NonNullList items = NonNullList.withSize(9, ItemStack.EMPTY); @@ -134,10 +134,23 @@ public class DispenserBlockEntity extends RandomizableContainerBlockEntity { @Override protected void setItems(NonNullList items) { this.items = items; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity } @Override protected AbstractContainerMenu createMenu(int id, Inventory player) { return new DispenserMenu(id, player, this); } + + // Leaves 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; + } + // Leaves 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 363d85c96bd3fb1a1945595df36e30bd6dd2fa4e..5f16810643b7567702b1101234571eafd2ded0f8 100644 --- a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java +++ b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java @@ -9,7 +9,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, org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity { // Leaves - Lithium Sleeping Block Entity private final ChestLidController chestLidController = new ChestLidController(); public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() { @Override @@ -57,11 +57,13 @@ public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity public static void lidAnimateTick(Level level, BlockPos pos, BlockState state, EnderChestBlockEntity blockEntity) { blockEntity.chestLidController.tickLid(); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) blockEntity.checkSleep(); // Leaves - Lithium Sleeping Block Entity } @Override public boolean triggerEvent(int id, int type) { if (id == 1) { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.sleepingTicker != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity this.chestLidController.shouldBeOpen(type > 0); return true; } else { @@ -95,4 +97,36 @@ public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity public float getOpenNess(float partialTicks) { return this.chestLidController.getOpenness(partialTicks); } + + // Leaves 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 the animation is finished, it will stay unchanged until the next triggerEvent, which may change shouldBeOpen + if (this.getOpenNess(0.0F) == this.getOpenNess(1.0F)) { + this.lithium$startSleeping(); + } + } + // Leaves 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 25ad8bb4099fefaf51f38a8bba70ea3594d0d914..3fd172f82216fa35c36e5f563819a6f01ffd4ea4 100644 --- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java +++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java @@ -27,8 +27,29 @@ import net.minecraft.world.level.block.state.BlockState; 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 { +// Leaves start - Lithium Sleeping Block Entity +import java.util.Objects; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.CompoundContainer; +import net.minecraft.server.level.ServerLevel; +import org.leavesmc.leaves.lithium.api.inventory.LithiumInventory; +import org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity; +import org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeListener; +import org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker; +import org.leavesmc.leaves.lithium.common.block.entity.inventory_comparator_tracking.ComparatorTracker; +import org.leavesmc.leaves.lithium.common.hopper.BlockStateOnlyInventory; +import org.leavesmc.leaves.lithium.common.hopper.HopperCachingState; +import org.leavesmc.leaves.lithium.common.hopper.HopperHelper; +import org.leavesmc.leaves.lithium.common.hopper.InventoryHelper; +import org.leavesmc.leaves.lithium.common.hopper.LithiumStackList; +import org.leavesmc.leaves.lithium.common.hopper.UpdateReceiver; +import org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionEntityMovementListener; +import org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionEntityMovementTracker; +import org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker; +import org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionItemEntityMovementTracker; +// Leaves end - Lithium Sleeping Block Entity + +public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper, SleepingBlockEntity, ChunkSectionEntityMovementListener, LithiumInventory, InventoryChangeListener, UpdateReceiver, InventoryChangeTracker { // Leaves - 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][]; @@ -76,7 +97,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Override public void updateTicks(final long fromTickOffset, final long fromRedstoneTimeOffset) { super.updateTicks(fromTickOffset, fromRedstoneTimeOffset); - if (this.tickedGameTime != Long.MIN_VALUE) { + if (this.tickedGameTime != Long.MIN_VALUE && this.tickedGameTime != Long.MAX_VALUE) { // Luminol - Lithium sleeping block entity for folia this.tickedGameTime += fromRedstoneTimeOffset; } } @@ -128,6 +149,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Override public void setBlockState(BlockState blockState) { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.level != null && !this.level.isClientSide() && blockState.getValue(HopperBlock.FACING) != this.getBlockState().getValue(HopperBlock.FACING)) this.invalidateCachedData(); // Leaves - Lithium Sleeping Block Entity super.setBlockState(blockState); this.facing = blockState.getValue(HopperBlock.FACING); } @@ -146,6 +168,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen boolean result = tryMoveItems(level, pos, state, blockEntity, () -> { return suckInItems(level, blockEntity); }); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) blockEntity.checkSleepingConditions(); // Leaves - Lithium Sleeping Block Entity if (!result && blockEntity.level.spigotConfig.hopperCheck > 1) { blockEntity.setCooldown(blockEntity.level.spigotConfig.hopperCheck); } @@ -196,18 +219,26 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen } else { if (!blockEntity.isOnCooldown() && state.getValue(HopperBlock.ENABLED)) { boolean flag = false; - final int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers - if (fullState != HOPPER_EMPTY) { // Paper - Perf: Optimize Hoppers + // final int fullState = getFullState(blockEntity); // Paper - Perf: Optimize Hoppers // Luminol - vanilla hopper + if (/*fullState != HOPPER_EMPTY*/!blockEntity.isEmpty()) { // Paper - Perf: Optimize Hoppers // Luminol - vanilla hopper flag = ejectItems(level, pos, blockEntity); } - if (fullState != HOPPER_IS_FULL || flag) { // Paper - Perf: Optimize Hoppers + if (/*fullState != HOPPER_IS_FULL || flag*/!blockEntity.inventoryFull()) { // Paper - Perf: Optimize Hoppers // Luminol - vanilla hopper flag |= validator.getAsBoolean(); // Paper - note: this is not a validator, it's what adds/sucks in items } if (flag) { blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot setChanged(level, pos, state); + // Leaves start - Lithium Sleeping Block Entity + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled + && !blockEntity.isOnCooldown() + && !blockEntity.isSleeping() + && !state.getValue(HopperBlock.ENABLED)) { + blockEntity.lithium$startSleeping(); + } + // Leaves end - Lithium Sleeping Block Entity return true; } } @@ -303,9 +334,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen origItemStack.setCount(originalItemCount - movedItemCount + remainingItemCount); } - IGNORE_TILE_UPDATES.set(true); // Folia - region threading + // IGNORE_TILE_UPDATES.set(true); // Folia - region threading // Luminol - vanilla hopper container.setItem(i, origItemStack); - IGNORE_TILE_UPDATES.set(false); // Folia - region threading + //IGNORE_TILE_UPDATES.set(false); // Folia - region threading // Luminol - vanilla hopper container.setChanged(); return true; } @@ -386,6 +417,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; // Leaves - Lithium Sleeping Block Entity } } @@ -429,67 +461,76 @@ 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 = me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled ? blockEntity.getInsertInventory(level) : getAttachedContainer(level, pos, blockEntity); // Leaves - Lithium Sleeping Block Entity if (attachedContainer == null) { return false; } else { Direction opposite = blockEntity.facing.getOpposite(); + // Leaves start - Lithium Sleeping Block Entity + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) { + Boolean res = lithiumInsert(level, pos, blockEntity, attachedContainer); + if (res != null) { + return res; + } + } + // Leaves end - Lithium Sleeping Block Entity if (isFullContainer(attachedContainer, opposite)) { return false; } else { // Paper start - Perf: Optimize Hoppers - return hopperPush(level, attachedContainer, opposite, blockEntity); - //for (int i = 0; i < blockEntity.getContainerSize(); i++) { - // ItemStack item = blockEntity.getItem(i); - // if (!item.isEmpty()) { - // int count = item.getCount(); - // // CraftBukkit start - Call event when pushing items into other inventories - // ItemStack original = item.copy(); - // org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( - // blockEntity.removeItem(i, level.spigotConfig.hopperAmount) - // ); // Spigot - - // org.bukkit.inventory.Inventory destinationInventory; - // // Have to special case large chests as they work oddly - // if (attachedContainer instanceof final net.minecraft.world.CompoundContainer compoundContainer) { - // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); - // } else if (attachedContainer.getOwner() != null) { - // destinationInventory = attachedContainer.getOwner().getInventory(); - // } else { - // destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer); - // } - - // org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent( - // blockEntity.getOwner().getInventory(), - // oitemstack, - // destinationInventory, - // true - // ); - // if (!event.callEvent()) { - // blockEntity.setItem(i, original); - // blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot - // return false; - // } - // int origCount = event.getItem().getAmount(); // Spigot - // ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite); - // // CraftBukkit end - - // if (itemStack.isEmpty()) { - // attachedContainer.setChanged(); - // return true; - // } - - // item.setCount(count); - // // Spigot start - // item.shrink(origCount - itemStack.getCount()); - // if (count <= level.spigotConfig.hopperAmount) { - // // Spigot end - // blockEntity.setItem(i, item); - // } - // } - //} - - //return false; + // Luminol start - vanilla hopper + // return hopperPush(level, attachedContainer, opposite, blockEntity); + for (int i = 0; i < blockEntity.getContainerSize(); i++) { + ItemStack item = blockEntity.getItem(i); + if (!item.isEmpty()) { + int count = item.getCount(); + // CraftBukkit start - Call event when pushing items into other inventories + ItemStack original = item.copy(); + org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( + blockEntity.removeItem(i, level.spigotConfig.hopperAmount) + ); // Spigot + + org.bukkit.inventory.Inventory destinationInventory; + // Have to special case large chests as they work oddly + if (attachedContainer instanceof final net.minecraft.world.CompoundContainer compoundContainer) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); + } else if (attachedContainer.getOwner() != null) { + destinationInventory = attachedContainer.getOwner().getInventory(); + } else { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(attachedContainer); + } + + org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent( + blockEntity.getOwner().getInventory(), + oitemstack, + destinationInventory, + true + ); + if (!event.callEvent()) { + blockEntity.setItem(i, original); + blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Delay hopper checks // Spigot + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite); + // CraftBukkit end + + if (itemStack.isEmpty()) { + attachedContainer.setChanged(); + return true; + } + + item.setCount(count); + // Spigot start + item.shrink(origCount - itemStack.getCount()); + if (count <= level.spigotConfig.hopperAmount) { + // Spigot end + blockEntity.setItem(i, item); + } + } + } + + return false; // Paper end - Perf: Optimize Hoppers } } @@ -543,10 +584,18 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen io.papermc.paper.threadedregions.RegionizedWorldData worldData = io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentRegionizedWorldData(); // Folia - region threading 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 = me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled ? getExtractInventory(level, hopper, blockPos, blockState) : getSourceContainer(level, hopper, blockPos, blockState); // Leaves - Lithium Sleeping Block Entity if (sourceContainer != null) { Direction direction = Direction.DOWN; worldData.skipPullModeEventFire = worldData.skipHopperEvents; // Paper - Perf: Optimize Hoppers // Folia - region threading + // Leaves start - Lithium Sleeping Block Entity + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) { + Boolean res = lithiumExtract(level, hopper, sourceContainer); + if (res != null) { + return res; + } + } + // Leaves end - Lithium Sleeping Block Entity for (int i : getSlots(sourceContainer, direction)) { if (tryTakeInItemFromSlot(hopper, sourceContainer, i, direction, level)) { // Spigot @@ -558,7 +607,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 : me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled ? lithiumGetInputItemEntities(level, hopper) : getItemsAtAndAbove(level, hopper)) { // Leaves - Lithium Sleeping Block Entity if (addItem(hopper, itemEntity)) { return true; } @@ -573,56 +622,57 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen ItemStack item = container.getItem(slot); if (!item.isEmpty() && canTakeItemFromContainer(hopper, container, item, slot, direction)) { // Paper start - Perf: Optimize Hoppers - return hopperPull(level, hopper, container, item, slot); - //int count = item.getCount(); - //// CraftBukkit start - Call event on collection of items from inventories into the hopper - //ItemStack original = item.copy(); - //org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( - // container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot - //); - - //org.bukkit.inventory.Inventory sourceInventory; - //// Have to special case large chests as they work oddly - //if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) { - // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); - //} else if (container.getOwner() != null) { - // sourceInventory = container.getOwner().getInventory(); - //} else { - // sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container); - //} - - //org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent( - // sourceInventory, - // oitemstack, - // hopper.getOwner().getInventory(), - // false - //); - - //if (!event.callEvent()) { - // container.setItem(slot, original); - - // if (hopper instanceof final HopperBlockEntity hopperBlockEntity) { - // hopperBlockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot - // } - - // return false; - //} - //int origCount = event.getItem().getAmount(); // Spigot - //ItemStack itemStack = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null); - //// CraftBukkit end - - //if (itemStack.isEmpty()) { - // container.setChanged(); - // return true; - //} - - //item.setCount(count); - //// Spigot start - //item.shrink(origCount - itemStack.getCount()); - //if (count <= level.spigotConfig.hopperAmount) { - // // Spigot end - // container.setItem(slot, item); - //} + // Luminol start - vanilla hopper + // return hopperPull(level, hopper, container, item, slot); + int count = item.getCount(); + // CraftBukkit start - Call event on collection of items from inventories into the hopper + ItemStack original = item.copy(); + org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror( + container.removeItem(slot, level.spigotConfig.hopperAmount) // Spigot + ); + + org.bukkit.inventory.Inventory sourceInventory; + // Have to special case large chests as they work oddly + if (container instanceof final net.minecraft.world.CompoundContainer compoundContainer) { + sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest(compoundContainer); + } else if (container.getOwner() != null) { + sourceInventory = container.getOwner().getInventory(); + } else { + sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventory(container); + } + + org.bukkit.event.inventory.InventoryMoveItemEvent event = new org.bukkit.event.inventory.InventoryMoveItemEvent( + sourceInventory, + oitemstack, + hopper.getOwner().getInventory(), + false + ); + + if (!event.callEvent()) { + container.setItem(slot, original); + + if (hopper instanceof final HopperBlockEntity hopperBlockEntity) { + hopperBlockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot + } + + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemStack = HopperBlockEntity.addItem(container, hopper, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), null); + // CraftBukkit end + + if (itemStack.isEmpty()) { + container.setChanged(); + return true; + } + + item.setCount(count); + // Spigot start + item.shrink(origCount - itemStack.getCount()); + if (count <= level.spigotConfig.hopperAmount) { + // Spigot end + container.setItem(slot, item); + } // Paper end - Perf: Optimize Hoppers } @@ -695,9 +745,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen stack = stack.split(destination.getMaxStackSize()); } // Spigot end - IGNORE_TILE_UPDATES.set(Boolean.TRUE); // Paper - Perf: Optimize Hoppers // Folia - region threading + //IGNORE_TILE_UPDATES.set(Boolean.TRUE); // Paper - Perf: Optimize Hoppers // Folia - region threading // Luminol - vanilla hopper destination.setItem(slot, stack); - IGNORE_TILE_UPDATES.set(Boolean.FALSE); // Paper - Perf: Optimize Hoppers // Folia - region threading + //IGNORE_TILE_UPDATES.set(Boolean.FALSE); // Paper - Perf: Optimize Hoppers // Folia - region threading // Luminol - vanilla hopper stack = leftover; // Paper - Make hoppers respect inventory max stack size flag = true; } else if (canMergeItems(item, stack)) { @@ -728,7 +778,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 @@ -787,7 +837,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Nullable public static Container getContainerAt(Level level, BlockPos pos) { - return getContainerAt(level, pos, level.getBlockState(pos), pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, true); // Paper - Optimize hoppers + return getContainerAt(level, pos, level.getBlockState(pos), pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, false); // Paper - Optimize hoppers // Luminol - vanilla hopper } @Nullable @@ -799,7 +849,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen private static Container getContainerAt(Level level, BlockPos pos, BlockState state, double x, double y, double z, final boolean optimizeEntities) { // Paper end - Perf: Optimize Hoppers Container blockContainer = getBlockContainer(level, pos, state); - if (blockContainer == null && (!optimizeEntities || !level.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())) { // Paper - Perf: Optimize Hoppers + if (blockContainer == null /*&& (!optimizeEntities || !level.paperConfig().hopper.ignoreOccludingBlocks || !state.getBukkitMaterial().isOccluding())*/) { // Paper - Perf: Optimize Hoppers // Luminol - vanilla hopper blockContainer = getEntityContainer(level, x, y, z); } @@ -856,6 +906,19 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen } public void setCooldown(int cooldownTime) { + // Leaves start - Lithium Sleeping Block Entity + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) { + if (cooldownTime == 7) { + if (this.tickedGameTime == Long.MAX_VALUE) { + this.sleepOnlyCurrentTick(); + } else { + this.wakeUpNow(); + } + } else if (cooldownTime > 0 && this.sleepingTicker != null) { + this.wakeUpNow(); + } + } + // Leaves end - Lithium Sleeping Block Entity this.cooldownTime = cooldownTime; } @@ -875,6 +938,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen @Override protected void setItems(NonNullList items) { this.items = items; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity } public static void entityInside(Level level, BlockPos pos, BlockState state, Entity entity, HopperBlockEntity blockEntity) { @@ -889,4 +953,760 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen protected AbstractContainerMenu createMenu(int id, Inventory player) { return new HopperMenu(id, player, this); } + + // Leaves start - Lithium Sleeping Block Entity + @Nullable private LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = null; + @Nullable private TickingBlockEntity sleepingTicker = null; + private long myModCountAtLastInsert, myModCountAtLastExtract, myModCountAtLastItemCollect; + private boolean skipNextSleepCheckAfterCooldown = false; + + private HopperCachingState.BlockInventory insertionMode = HopperCachingState.BlockInventory.UNKNOWN; + private HopperCachingState.BlockInventory extractionMode = HopperCachingState.BlockInventory.UNKNOWN; + + //The currently used block inventories + @Nullable + private Container insertBlockInventory, extractBlockInventory; + + //The currently used inventories (optimized type, if not present, skip optimizations) + @Nullable + private LithiumInventory insertInventory, extractInventory; + @Nullable //Null iff corresp. LithiumInventory field is null + private LithiumStackList insertStackList, extractStackList; + //Mod count used to avoid transfer attempts that are known to fail (no change since last attempt) + 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; + + LithiumStackList thisStackList = InventoryHelper.getLithiumStackList(this); + + if (this.extractionMode != HopperCachingState.BlockInventory.BLOCK_STATE && thisStackList.getFullSlots() != thisStackList.size()) { + if (this.extractionMode == HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + Container blockInventory = this.extractBlockInventory; + if (this.extractStackList != null && + blockInventory instanceof InventoryChangeTracker) { + if (!this.extractStackList.maybeSendsComparatorUpdatesOnFailedExtract() || (blockInventory instanceof ComparatorTracker comparatorTracker && !comparatorTracker.lithium$hasAnyComparatorNearby())) { + listenToExtractTracker = true; + } else { + return; + } + } else { + return; + } + } else if (this.extractionMode == 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 != HopperCachingState.BlockInventory.BLOCK_STATE && 0 < thisStackList.getOccupiedSlots()) { + if (this.insertionMode == HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + Container blockInventory = this.insertBlockInventory; + if (this.insertStackList != null && blockInventory instanceof InventoryChangeTracker) { + listenToInsertTracker = true; + } else { + return; + } + } else if (this.insertionMode == HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY) { + BlockState hopperState = this.getBlockState(); + listenToInsertEntities = true; + } else { + return; + } + } + + if (listenToExtractTracker) { + ((InventoryChangeTracker) this.extractBlockInventory).listenForContentChangesOnce(this.extractStackList, this); + } + if (listenToInsertTracker) { + ((InventoryChangeTracker) this.insertBlockInventory).listenForContentChangesOnce(this.insertStackList, this); + } + if (listenToInsertEntities) { + if (this.insertInventoryEntityTracker == null || this.insertInventoryEntityTracker.isEmpty()) { + return; + } + ChunkSectionEntityMovementTracker.listenToEntityMovementOnce(this, insertInventoryEntityTracker); + } + if (listenToExtractEntities) { + if (this.extractInventoryEntityTracker == null || this.extractInventoryEntityTracker.isEmpty()) { + return; + } + ChunkSectionEntityMovementTracker.listenToEntityMovementOnce(this, extractInventoryEntityTracker); + } + if (listenToItemEntities) { + if (this.collectItemEntityTracker == null || this.collectItemEntityTracker.isEmpty()) { + return; + } + 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(LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper) { + this.tickWrapper = tickWrapper; + this.lithium$setSleepingTicker(null); + } + + @Override + public @Nullable LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { + return tickWrapper; + } + + @Override + public boolean lithium$startSleeping() { + if (this.isSleeping()) { + return false; + } + + LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = this.lithium$getTickWrapper(); + if (tickWrapper != null) { + this.lithium$setSleepingTicker(tickWrapper.ticker); + tickWrapper.setLithiumSlept(tickWrapper.ticker); + tickWrapper.rebind(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) { + //Clear the block inventory cache (composter inventories and no inventory present) on block update / observer update + if (fromAbove) { + if (this.extractionMode == HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY || this.extractionMode == HopperCachingState.BlockInventory.BLOCK_STATE) { + this.invalidateBlockExtractionData(); + } + } else { + if (this.insertionMode == HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY || this.insertionMode == HopperCachingState.BlockInventory.BLOCK_STATE) { + this.invalidateBlockInsertionData(); + } + } + } + + @Override + public void lithium$invalidateCacheOnUndirectedNeighborUpdate() { + if (this.extractionMode == HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY || this.extractionMode == HopperCachingState.BlockInventory.BLOCK_STATE) { + this.invalidateBlockExtractionData(); + } + if (this.insertionMode == HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY || this.insertionMode == 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 = 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 ServerLevel) { + if (this.insertInventoryEntityTracker != null) { + ChunkSectionEntityMovementTracker.unregister(this.insertInventoryEntityTracker); + this.insertInventoryEntityTracker = null; + this.insertInventoryEntityBox = null; + this.insertInventoryEntityFailedSearchTime = 0L; + } + } + + if (this.insertionMode == HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + assert this.insertBlockInventory != null; + ((InventoryChangeTracker) this.insertBlockInventory).stopListenForMajorInventoryChanges(this); + } + this.invalidateBlockInsertionData(); + } + + private void invalidateExtractionData() { + if (this.level instanceof ServerLevel) { + if (this.extractInventoryEntityTracker != null) { + ChunkSectionEntityMovementTracker.unregister(this.extractInventoryEntityTracker); + this.extractInventoryEntityTracker = null; + this.extractInventoryEntityBox = null; + this.extractInventoryEntityFailedSearchTime = 0L; + } + if (this.collectItemEntityTracker != null) { + ChunkSectionEntityMovementTracker.unregister(this.collectItemEntityTracker); + this.collectItemEntityTracker = null; + this.collectItemEntityBox = null; + this.collectItemEntityTrackerWasEmpty = false; + } + } + if (this.extractionMode == HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + assert this.extractBlockInventory != null; + ((InventoryChangeTracker) this.extractBlockInventory).stopListenForMajorInventoryChanges(this); + } + this.invalidateBlockExtractionData(); + } + + private void invalidateBlockExtractionData() { + this.extractionMode = 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 == HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY) { + return null; + } else if (this.extractionMode == HopperCachingState.BlockInventory.BLOCK_STATE) { + return blockInventory; + } else if (this.extractionMode == HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + return blockInventory; + } else if (this.extractionMode == HopperCachingState.BlockInventory.BLOCK_ENTITY) { + BlockEntity blockEntity = (BlockEntity) Objects.requireNonNull(blockInventory); + //Movable Block Entity compatibility - position comparison + BlockPos pos = blockEntity.getBlockPos(); + if (!(blockEntity).isRemoved() && pos.equals(extractBlockPos)) { + LithiumInventory optimizedInventory; + if ((optimizedInventory = this.extractInventory) != null) { + LithiumStackList insertInventoryStackList = 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.extractStackList) { + return optimizedInventory; + } else { + this.invalidateBlockExtractionData(); + } + } else { + return blockInventory; + } + } + } + + //No Cached Inventory: Get like vanilla and cache + blockInventory = getBlockContainer(world, extractBlockPos, extractBlockState); + blockInventory = HopperHelper.replaceDoubleInventory(blockInventory); + this.cacheExtractBlockInventory(blockInventory); + return blockInventory; + } + + public @Nullable Container lithium$getInsertBlockInventory(Level world) { + Container blockInventory = this.insertBlockInventory; + if (this.insertionMode == HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY) { + return null; + } else if (this.insertionMode == HopperCachingState.BlockInventory.BLOCK_STATE) { + return blockInventory; + } else if (this.insertionMode == HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY) { + return blockInventory; + } else if (this.insertionMode == HopperCachingState.BlockInventory.BLOCK_ENTITY) { + BlockEntity blockEntity = (BlockEntity) 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)) { + LithiumInventory optimizedInventory; + if ((optimizedInventory = this.insertInventory) != null) { + LithiumStackList insertInventoryStackList = 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 = 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 (ChunkSectionEntityMovementTracker.isUnchangedSince(this.insertInventoryEntityFailedSearchTime, this.insertInventoryEntityTracker)) { + this.insertInventoryEntityFailedSearchTime = this.tickedGameTime; + return null; + } + this.insertInventoryEntityFailedSearchTime = Long.MIN_VALUE; + this.shouldCheckSleep = false; + + List inventoryEntities = 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 LithiumInventory optimizedInventory) { + LithiumStackList insertInventoryStackList = InventoryHelper.getLithiumStackList(optimizedInventory); + if (inventory != this.insertInventory || this.insertStackList != insertInventoryStackList) { + this.cacheInsertLithiumInventory(optimizedInventory); + } + } + + return inventory; + } + + private void initCollectItemEntityTracker() { + assert this.level instanceof ServerLevel; + AABB inputBox = this.getSuckAabb().move(this.worldPosition.getX(), this.worldPosition.getY(), this.worldPosition.getZ()); + this.collectItemEntityBox = inputBox; + this.collectItemEntityTracker = + ChunkSectionItemEntityMovementTracker.registerAt( + (ServerLevel) this.level, + inputBox + ); + this.collectItemEntityAttemptTime = Long.MIN_VALUE; + } + + private void initExtractInventoryTracker(Level world) { + assert world instanceof 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 = + ChunkSectionInventoryEntityTracker.registerAt( + (ServerLevel) this.level, + this.extractInventoryEntityBox + ); + this.extractInventoryEntityFailedSearchTime = Long.MIN_VALUE; + } + + private void initInsertInventoryTracker(Level world) { + assert world instanceof 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 = + ChunkSectionInventoryEntityTracker.registerAt( + (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 (ChunkSectionEntityMovementTracker.isUnchangedSince(this.extractInventoryEntityFailedSearchTime, this.extractInventoryEntityTracker)) { + this.extractInventoryEntityFailedSearchTime = this.tickedGameTime; + return null; + } + this.extractInventoryEntityFailedSearchTime = Long.MIN_VALUE; + this.shouldCheckSleep = false; + + List inventoryEntities = 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 LithiumInventory optimizedInventory) { + LithiumStackList extractInventoryStackList = 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 LithiumInventory optimizedInventory) { + this.cacheInsertLithiumInventory(optimizedInventory); + } else { + this.insertInventory = null; + this.insertStackList = null; + this.insertStackListModCount = 0; + } + + if (insertInventory instanceof BlockEntity || insertInventory instanceof CompoundContainer) { + this.insertBlockInventory = insertInventory; + if (insertInventory instanceof InventoryChangeTracker) { + this.insertionMode = HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY; + ((InventoryChangeTracker) insertInventory).listenForMajorInventoryChanges(this); + } else { + this.insertionMode = HopperCachingState.BlockInventory.BLOCK_ENTITY; + } + } else { + if (insertInventory == null) { + this.insertBlockInventory = null; + this.insertionMode = HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY; + } else { + this.insertBlockInventory = insertInventory; + this.insertionMode = insertInventory instanceof BlockStateOnlyInventory ? HopperCachingState.BlockInventory.BLOCK_STATE : HopperCachingState.BlockInventory.UNKNOWN; + } + } + } + + private void cacheInsertLithiumInventory(LithiumInventory optimizedInventory) { + LithiumStackList insertInventoryStackList = InventoryHelper.getLithiumStackList(optimizedInventory); + this.insertInventory = optimizedInventory; + this.insertStackList = insertInventoryStackList; + this.insertStackListModCount = insertInventoryStackList.getModCount() - 1; + } + + private void cacheExtractLithiumInventory(LithiumInventory optimizedInventory) { + LithiumStackList extractInventoryStackList = 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 LithiumInventory optimizedInventory) { + this.cacheExtractLithiumInventory(optimizedInventory); + } else { + this.extractInventory = null; + this.extractStackList = null; + this.extractStackListModCount = 0; + } + + if (extractInventory instanceof BlockEntity || extractInventory instanceof CompoundContainer) { + this.extractBlockInventory = extractInventory; + if (extractInventory instanceof InventoryChangeTracker) { + this.extractionMode = HopperCachingState.BlockInventory.REMOVAL_TRACKING_BLOCK_ENTITY; + ((InventoryChangeTracker) extractInventory).listenForMajorInventoryChanges(this); + } else { + this.extractionMode = HopperCachingState.BlockInventory.BLOCK_ENTITY; + } + } else { + if (extractInventory == null) { + this.extractBlockInventory = null; + this.extractionMode = HopperCachingState.BlockInventory.NO_BLOCK_INVENTORY; + } else { + this.extractBlockInventory = extractInventory; + this.extractionMode = extractInventory instanceof BlockStateOnlyInventory ? HopperCachingState.BlockInventory.BLOCK_STATE : 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 = InventoryHelper.getLithiumStackList(hopperBlockEntity).getModCount(); + + if ((hopperBlockEntity.collectItemEntityTrackerWasEmpty || hopperBlockEntity.myModCountAtLastItemCollect == modCount) && + ChunkSectionEntityMovementTracker.isUnchangedSince(hopperBlockEntity.collectItemEntityAttemptTime, hopperBlockEntity.collectItemEntityTracker)) { + hopperBlockEntity.collectItemEntityAttemptTime = hopperBlockEntity.tickedGameTime; + return java.util.Collections.emptyList(); + } + + hopperBlockEntity.myModCountAtLastItemCollect = modCount; + hopperBlockEntity.shouldCheckSleep = false; + + List itemEntities = 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; + } + + LithiumStackList hopperStackList = 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(); + + var currentWorldData = world.getCurrentWorldData(); // Luminol - Region threading for lithium sleeping block entity + currentWorldData.skipPushModeEventFire = currentWorldData.skipHopperEvents; // Luminol + //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 (!currentWorldData.skipPushModeEventFire && canTakeItemFromContainer(insertInventory, hopperBlockEntity, transferStack, i, Direction.DOWN)) { // Luminol + transferStack = callPushMoveEvent(insertInventory, transferStack, hopperBlockEntity); + if (transferStack == null) { // cancelled + break; + } + } + boolean transferSuccess = 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 + } + + LithiumStackList hopperStackList = InventoryHelper.getLithiumStackList(hopperBlockEntity); + LithiumStackList fromStackList = hopperBlockEntity.extractStackList; + + if (hopperStackList.getModCount() == hopperBlockEntity.myModCountAtLastExtract) { + if (fromStackList.getModCount() == hopperBlockEntity.extractStackListModCount) { + if (!(from instanceof ComparatorTracker comparatorTracker) || comparatorTracker.lithium$hasAnyComparatorNearby()) { + //noinspection CollectionAddedToSelf + fromStackList.runComparatorUpdatePatternOnFailedExtract(fromStackList, from); + } + return false; + } + } + + var currentWorldData = world.getCurrentWorldData(); // Luminol - Region threading for lithium sleeping block entity + 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 (!currentWorldData.skipPullModeEventFire) { // Luminol + 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 = 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; + } + // Leaves end - Lithium Sleeping Block Entity } diff --git a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java index ebea67223ce1d350087c73dff0cc3fe6d7b47ca0..5203e85b761122b01569221f22db55f7060d97ee 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, org.leavesmc.leaves.lithium.common.block.entity.inventory_change_tracking.InventoryChangeTracker, org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity, org.leavesmc.leaves.lithium.api.inventory.LithiumInventory { // Leaves - Lithium Sleeping Block Entity public static final int COLUMNS = 9; public static final int ROWS = 3; public static final int CONTAINER_SIZE = 27; @@ -134,6 +134,7 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl doNeighborUpdates(level, pos, state); } } + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.animationStatus == ShulkerBoxBlockEntity.AnimationStatus.CLOSED && this.progressOld == 0.0f && this.progress == 0.0f) this.lithium$startSleeping(); // Leaves - Lithium Sleeping Block Entity } public ShulkerBoxBlockEntity.AnimationStatus getAnimationStatus() { @@ -174,6 +175,7 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl @Override public boolean triggerEvent(int id, int type) { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.sleepingTicker != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity if (id == 1) { this.openCount = type; if (type == 0) { @@ -265,6 +267,7 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl @Override protected void setItems(NonNullList items) { this.itemStacks = items; + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity } @Override @@ -306,4 +309,39 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl OPENED, CLOSING; } + + // Leaves 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; + } + // Leaves end - Lithium Sleeping Block Entity } diff --git a/net/minecraft/world/level/block/entity/TickingBlockEntity.java b/net/minecraft/world/level/block/entity/TickingBlockEntity.java index c8facee29ee08e0975528083f89b64f0b593957f..47d1032e224e1b946ccf67a0a744661e98366295 100644 --- a/net/minecraft/world/level/block/entity/TickingBlockEntity.java +++ b/net/minecraft/world/level/block/entity/TickingBlockEntity.java @@ -12,4 +12,15 @@ public interface TickingBlockEntity { String getType(); BlockEntity getTileEntity(); // Folia - region threading + + // Luminol start - region threading for sleeping block entity + @org.jetbrains.annotations.Nullable + default TickingBlockEntity getLithiumSlept() { + return null; + } + + default void setLithiumSlept(@org.jetbrains.annotations.Nullable TickingBlockEntity slept) {} + + default void updateTicksForLithium(long redstoneGameTimeOffset) {} + // Luminol end } diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java index 834e27ef2f7b342b074ff9e1e390e02f3ca1c399..e9749b66239d7562ecf22002bfbaa95df197050f 100644 --- a/net/minecraft/world/level/block/state/BlockBehaviour.java +++ b/net/minecraft/world/level/block/state/BlockBehaviour.java @@ -85,7 +85,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, org.leavesmc.leaves.lithium.common.block.entity.ShapeUpdateHandlingBlockBehaviour { // Leaves - 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 }; @@ -157,6 +157,7 @@ public abstract class BlockBehaviour implements FeatureElement { BlockState neighborState, RandomSource random ) { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$handleShapeUpdate(level, state, pos, neighborPos, neighborState); // Leaves - Lithium Sleeping Block Entity /* Triggers when a shape update (= update that observers can detect) is sent */ return state; } diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java index 7add73f2dd957374661affebfa2b0102d99363f8..0d906776e10605ecfe7c681dbf313cf25080d2e8 100644 --- a/net/minecraft/world/level/chunk/LevelChunk.java +++ b/net/minecraft/world/level/chunk/LevelChunk.java @@ -891,12 +891,14 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p (pos, ticker1) -> { TickingBlockEntity tickingBlockEntity = this.createTicker(blockEntity, ticker); if (ticker1 != null) { + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && blockEntity instanceof org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity sleepingBlockEntity) sleepingBlockEntity.lithium$setTickWrapper(ticker1); // Leaves - Lithium Sleeping Block Entity ticker1.rebind(tickingBlockEntity); return (LevelChunk.RebindableTickingBlockEntityWrapper)ticker1; } else if (this.isInLevel()) { LevelChunk.RebindableTickingBlockEntityWrapper rebindableTickingBlockEntityWrapper = new LevelChunk.RebindableTickingBlockEntityWrapper( tickingBlockEntity ); + if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && blockEntity instanceof org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity sleepingBlockEntity) sleepingBlockEntity.lithium$setTickWrapper(rebindableTickingBlockEntityWrapper); // Leaves - Lithium Sleeping Block Entity this.level.addBlockEntityTicker(rebindableTickingBlockEntityWrapper); return rebindableTickingBlockEntityWrapper; } else { @@ -1000,14 +1002,15 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p void run(LevelChunk chunk); } - static class RebindableTickingBlockEntityWrapper implements TickingBlockEntity { - private TickingBlockEntity ticker; + public static class RebindableTickingBlockEntityWrapper implements TickingBlockEntity { // Leaves - default -> public + public TickingBlockEntity ticker; // Leaves - private -> public + @org.jetbrains.annotations.Nullable private TickingBlockEntity slept; // Luminol - region threading for sleeping block entity RebindableTickingBlockEntityWrapper(TickingBlockEntity ticker) { this.ticker = ticker; } - void rebind(TickingBlockEntity ticker) { + public void rebind(TickingBlockEntity ticker) { // Leaves - default -> public this.ticker = ticker; } @@ -1042,6 +1045,26 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p public String toString() { return this.ticker + " "; } + + // Luminol start - region threading for sleeping block entity + @Override + public @org.jetbrains.annotations.Nullable TickingBlockEntity getLithiumSlept() { + return this.slept; + } + + @Override + public void setLithiumSlept(@org.jetbrains.annotations.Nullable TickingBlockEntity slept) { + this.slept = slept; + } + + @Override + public void updateTicksForLithium(long redstoneGameTimeOffset) { + if (this.ticker != null) { + this.ticker.updateTicksForLithium(redstoneGameTimeOffset); + } + } + + // Luminol end } @FunctionalInterface