Files
Shiroha/shiroha-server/minecraft-patches/features/0055-Leaves-Lithium-Sleeping-Block-Entity.patch
T
NanaChiyo0721 1bb895c165
Shiroha CI / build (push) Canceled after 0s
Shiroha CI / Event File (push) Canceled after 0s
Fix incorrect ticket lock size computing
Yeah this would fully solve the issue of chunk unloading race condition

The same fix was already initially added at "Force clamp grid-exponent to 1~6"
2026-08-09 14:57:32 +08:00

2740 lines
149 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <mrhua269@gmail.com>
Date: Thu, 9 Jul 2026 12:00:19 +0800
Subject: [PATCH] Leaves Lithium Sleeping Block Entity
As a part of 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 c04dbcd615c97cb6dcb5f14fec88daf73e6e9b4a..53970fff0c119b190195e07823d5796a99de9c3b 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -65,6 +65,10 @@ import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import java.util.function.Predicate;
+// Shiroha start - imports for lithium sleeping block entity
+import org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionInventoryEntityTracker;
+import org.leavesmc.leaves.lithium.common.tracking.entity.ChunkSectionItemEntityMovementTracker;
+// Shiroha end
public final class RegionizedWorldData {
@@ -75,6 +79,32 @@ public final class RegionizedWorldData {
public static final RegionizedData.RegioniserCallback<RegionizedWorldData> REGION_CALLBACK = new RegionizedData.RegioniserCallback<>() {
@Override
public void merge(final RegionizedWorldData from, final RegionizedWorldData into, final long fromTickOffset) {
+ // Shiroha start - Region threading for 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);
+ }
+ // Shiroha end
// connections
for (final Connection conn : from.connections) {
into.connections.add(conn);
@@ -111,16 +141,22 @@ public final class RegionizedWorldData {
from.fluidLevelTicks.merge(into.fluidLevelTicks, fromRedstoneTimeOffset);
// tile entity ticking
- for (final TickingBlockEntity tileEntityWrapped : from.pendingBlockEntityTickers) {
+ for (TickingBlockEntity tileEntityWrapped : from.pendingBlockEntityTickers) { // Shiroha - Region threading for lithium sleeping block entity
into.pendingBlockEntityTickers.add(tileEntityWrapped);
- final BlockEntity tileEntity = tileEntityWrapped.getTileEntity();
+ BlockEntity tileEntity = tileEntityWrapped.getTileEntity(); // Shiroha - Region threading for lithium sleeping block entity
+ // Shiroha start - Region threading for lithium sleeping block entity
+ tileEntityWrapped.updateTicksForLithium(fromRedstoneTimeOffset);
+ // Shiroha end - 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) { // Shiroha - Region threading for lithium sleeping block entity
into.blockEntityTickers.add(tileEntityWrapped);
- final BlockEntity tileEntity = tileEntityWrapped.getTileEntity();
+ BlockEntity tileEntity = tileEntityWrapped.getTileEntity(); // Shiroha - Region threading for lithium sleeping block entity
+ // Shiroha start - Region threading for lithium sleeping block entity
+ tileEntityWrapped.updateTicksForLithium(fromRedstoneTimeOffset);
+ // Shiroha end - Region threading for lithium sleeping block entity
if (tileEntity != null) {
tileEntity.updateTicks(fromTickOffset, fromRedstoneTimeOffset);
}
@@ -164,6 +200,37 @@ public final class RegionizedWorldData {
public void split(final RegionizedWorldData from, final int chunkToRegionShift,
final Long2ReferenceOpenHashMap<RegionizedWorldData> regionToData,
final ReferenceOpenHashSet<RegionizedWorldData> dataSet) {
+ // Shiroha start - Region threading for 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);
+ }
+ // Shiroha end
// connections
for (final Connection conn : from.connections) {
final ServerPlayer player = conn.getPlayer();
@@ -237,8 +304,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) { // Shiroha - Region threading for lithium sleeping block entity
+ BlockPos pos = tileEntity.getPos(); // Shiroha - Region threading for lithium sleeping block entity
+ if (pos == null) pos = tileEntity.getTileEntity().getBlockPos(); // Shiroha - Region threading for lithium sleeping block entity
final int chunkX = pos.getX() >> 4;
final int chunkZ = pos.getZ() >> 4;
@@ -248,8 +316,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) { // Shiroha - Region threading for lithium sleeping block entity
+ BlockPos pos = tileEntity.getPos(); // Shiroha - Region threading for lithium sleeping block entity
+ if (pos == null) pos = tileEntity.getTileEntity().getBlockPos(); // Shiroha - Region threading for lithium sleeping block entity
final int chunkX = pos.getX() >> 4;
final int chunkZ = pos.getZ() >> 4;
@@ -478,6 +547,11 @@ public final class RegionizedWorldData {
public final io.papermc.paper.redstone.RedstoneWireTurbo turbo;
// Environment attribute system
public Reference2ObjectOpenHashMap<EnvironmentAttribute<?>, EnvironmentAttributeSystem.ValueSampler<?>> attributeSamplers;
+ // Shiroha start - Region threading for lithium sleeping block entity
+ // Lithium
+ public final Map<Long, ChunkSectionInventoryEntityTracker> containerEntityMovementTrackerMap = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>();
+ public final Map<Long, ChunkSectionItemEntityMovementTracker> itemEntityMovementTrackerMap = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>();
+ // Shiroha end
public final TickRegions.TickRegionData regionData;
diff --git a/net/minecraft/core/NonNullList.java b/net/minecraft/core/NonNullList.java
index 8fd27a342f7302284c48b3db7d085bb692e08cdc..8992e13df499333235e1a51b725c0ad3ccd0284d 100644
--- a/net/minecraft/core/NonNullList.java
+++ b/net/minecraft/core/NonNullList.java
@@ -8,7 +8,7 @@ import java.util.Objects;
import org.jspecify.annotations.Nullable;
public class NonNullList<E> extends AbstractList<E> {
- private final List<E> list;
+ public final List<E> list; // Leaves - private -> public
private final @Nullable E defaultValue;
public static <E> NonNullList<E> create() {
diff --git a/net/minecraft/core/component/PatchedDataComponentMap.java b/net/minecraft/core/component/PatchedDataComponentMap.java
index 08ea8107fc8cbec3742278ab5cf03ef9048720f9..e1397bb21b20a4be1223d12eb1f50677cd9f39d5 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 org.jspecify.annotations.Nullable;
-public final class PatchedDataComponentMap implements DataComponentMap {
+public final class PatchedDataComponentMap implements DataComponentMap, org.leavesmc.leaves.lithium.common.util.change_tracking.ChangePublisher<net.minecraft.core.component.PatchedDataComponentMap> { // Leaves - Lithium Sleeping Block Entity
private final DataComponentMap prototype;
private Reference2ObjectMap<DataComponentType<?>, Optional<?>> patch;
private boolean copyOnWrite;
@@ -137,6 +137,7 @@ public final class PatchedDataComponentMap implements DataComponentMap {
}
private void ensureMapOwnership() {
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.copyOnWrite && this.subscriber != null) this.subscriber.lithium$notify((PatchedDataComponentMap) (Object) this, 0); // Leaves - Lithium Sleeping Block Entity (only notify when actually copying, matches Lithium mixin semantics)
if (this.copyOnWrite) {
this.patch = new Reference2ObjectArrayMap<>(this.patch);
this.copyOnWrite = false;
@@ -233,4 +234,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<net.minecraft.core.component.PatchedDataComponentMap> subscriber;
+
+ @Override
+ public void lithium$subscribe(org.leavesmc.leaves.lithium.common.util.change_tracking.ChangeSubscriber<net.minecraft.core.component.PatchedDataComponentMap> 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<net.minecraft.core.component.PatchedDataComponentMap> 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 f2c5a6ce769d8a5fdb4b836eae6a61a4da91cb3f..7574c32c59a3e31973c58b54c5e352aaa161158f 100644
--- a/net/minecraft/server/commands/data/EntityDataAccessor.java
+++ b/net/minecraft/server/commands/data/EntityDataAccessor.java
@@ -57,6 +57,7 @@ public class EntityDataAccessor implements DataAccessor {
try (ProblemReporter.ScopedCollector reporter = new ProblemReporter.ScopedCollector(this.entity.problemPath(), LOGGER)) {
this.entity.load(TagValueInput.create(reporter, this.entity.registryAccess(), tag));
this.entity.setUUID(uuid);
+ if (io.nanachiyo0721.shiroha.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/world/Container.java b/net/minecraft/world/Container.java
index 6e08d50794c4af4405f3e164a3fd46f376b3f78f..dd2d0940eece3e85078e574f66f71a4c497d75fd 100644
--- a/net/minecraft/world/Container.java
+++ b/net/minecraft/world/Container.java
@@ -16,7 +16,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.jspecify.annotations.Nullable;
-public interface Container extends Clearable, Iterable<ItemStack>, SlotProvider {
+public interface Container extends Clearable, Iterable<ItemStack>, SlotProvider, 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 de6a71616f62c0b1b373840102012cec06703081..ca042e76eb240e0f772dc39fb6aa41c2ceb19f63 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -324,7 +324,7 @@ public abstract class Entity
private static final EntityDataAccessor<Boolean> DATA_NO_GRAVITY = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.BOOLEAN);
protected static final EntityDataAccessor<Pose> DATA_POSE = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.POSE);
public static final EntityDataAccessor<Integer> 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 needsSync;
public boolean syncPosition;
@@ -4463,11 +4463,13 @@ public abstract class Entity
}
protected Entity transformForAsyncTeleport(ServerLevel destination, Vec3 pos, Float yaw, Float pitch, Vec3 velocity) {
+ final boolean toSameWorld = this.level != destination; // Shiroha - Region threading for lithium 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(); // Shiroha - Region threading for lithium sleeping block entity
if (copy instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon dragon) dragon.syncDragonPartsAfterTeleportTransform(); // Shiroha - Fix dragon part desync
// 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
@@ -6034,8 +6036,29 @@ public abstract class Entity
this.setBoundingBox(this.makeBoundingBox());
}
// Paper end - Block invalid positions and bounding box
+ this.notifyLithiumTrackerIfNeeded(); // Shiroha - Leaves lithium sleeping block entity
}
+ // Shiroha start - Region threading for lithium sleeping block entity
+ private void notifyLithiumTrackerIfNeeded() {
+ // Leaves start - Lithium Sleeping Block Entity
+ if (!io.nanachiyo0721.shiroha.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()); // Shiroha - Region threading for lithium 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()); // Shiroha - Region threading for lithium sleeping block entity
+ }
+ // Leaves end - Lithium Sleeping Block Entity
+ }
+ // Shiroha end
+
public void checkDespawn() {
}
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
index 312c3974bdf27e18b79685a0d0c96cd5c3c134a1..d6abaaa951b12135b00e543d283d7714a575d797 100644
--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -35,8 +35,12 @@ import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
+// 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<ItemEntity>, ChangeSubscriber.CountChangeSubscriber<ItemStack> { // Leaves - Lithium Sleeping Block Entity
private static final EntityDataAccessor<ItemStack> 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;
@@ -532,6 +536,25 @@ public class ItemEntity extends Entity implements TraceableEntity {
}
public void setItem(final ItemStack itemStack) {
+ // Leaves start - Lithium Sleeping Block Entity
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.subscriber != null) {
+ ItemStack oldStack = this.getItem();
+ if (oldStack != itemStack) {
+ if (!oldStack.isEmpty()) {
+ oldStack.lithium$unsubscribe(this);
+ }
+
+ if (!itemStack.isEmpty()) {
+ itemStack.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, itemStack);
this.despawnRate = this.level().paperConfig().entities.spawning.altItemDespawnRate.enabled ? this.level().paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(itemStack.getItem(), this.level().spigotConfig.itemDespawnRate) : this.level().spigotConfig.itemDespawnRate; // Paper - Alternative item-despawn-rate
}
@@ -599,4 +622,76 @@ public class ItemEntity extends Entity implements TraceableEntity {
public @Nullable SlotAccess getSlot(final int slot) {
return slot == 0 ? SlotAccess.of(this::getItem, this::setItem) : super.getSlot(slot);
}
+
+ // Leaves start - Lithium Sleeping Block Entity
+ private ChangeSubscriber<ItemEntity> 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<ItemEntity> 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<ItemEntity> 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<ItemEntity> countChangeSubscriber) {
+ countChangeSubscriber.lithium$notifyCount(this, this.subscriberData, newCount);
+ }
+ }
+ // Leaves end - Lithium Sleeping Block Entity
}
diff --git a/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java b/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java
index d3651110e15e8e1ad530df4bb4c05dbc73a79a63..d87dcb72fd5d2b22eafc1a444d4fc3807595b6a5 100644
--- a/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java
+++ b/net/minecraft/world/entity/vehicle/minecart/AbstractMinecartContainer.java
@@ -23,7 +23,7 @@ import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
-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<ItemStack> itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513
private @Nullable ResourceKey<LootTable> lootTable;
private long lootTableSeed;
@@ -217,4 +217,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<net.minecraft.world.item.ItemStack> getInventoryLithium() {
+ return itemStacks;
+ }
+
+ @Override
+ public void setInventoryLithium(net.minecraft.core.NonNullList<net.minecraft.world.item.ItemStack> 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 136e018d83c5be90894358a97b23f5fcfa6c0d38..369d71f317f771fddbe9939e50a77562444209ec 100644
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -893,6 +893,7 @@ public abstract class AbstractContainerMenu {
float totalPercent = 0.0F;
+ if (io.nanachiyo0721.shiroha.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 itemStack = container.getItem(i);
if (!itemStack.isEmpty()) {
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index dc1e9acce122f3ebd7bf2b03150b4045db9493df..b507be6db077a0c9b77270f3359b0908c9298c71 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -98,8 +98,12 @@ import org.apache.commons.lang3.function.TriConsumer;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.jspecify.annotations.Nullable;
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, ItemInstance {
+public final class ItemStack implements DataComponentHolder, ItemInstance, ChangePublisher<net.minecraft.world.item.ItemStack>, ChangeSubscriber<PatchedDataComponentMap> { // Leaves - Lithium Sleeping Block Entity
private static final List<Component> 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),
@@ -965,6 +969,7 @@ public final class ItemStack implements DataComponentHolder, ItemInstance {
// CraftBukkit end
public <T> @Nullable T set(final DataComponentType<T> type, final @Nullable T value) {
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && type == DataComponents.ENCHANTMENTS && this.subscriber instanceof ChangeSubscriber.EnchantmentSubscriber<ItemStack> enchantmentSubscriber) enchantmentSubscriber.lithium$notifyAfterEnchantmentChange(this, this.subscriberData); // Leaves - Lithium Sleeping Block Entity
return this.components.set(type, value);
}
@@ -1304,6 +1309,23 @@ public final class ItemStack implements DataComponentHolder, ItemInstance {
}
public void setCount(final int count) {
+ // Leaves start - Lithium Sleeping Block Entity
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && count != this.count) {
+ if (this.subscriber instanceof ChangeSubscriber.CountChangeSubscriber<ItemStack> 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;
}
@@ -1371,4 +1393,90 @@ public final class ItemStack implements DataComponentHolder, ItemInstance {
.or(() -> Optional.ofNullable(this.getItem().getItemDamageSource(attacker)))
.orElseGet(attacker::createDamageSource);
}
+
+ // Leaves start - Lithium Sleeping Block Entity
+ private ChangeSubscriber<ItemStack> subscriber;
+ private int subscriberData;
+
+ @Override
+ public void lithium$subscribe(ChangeSubscriber<ItemStack> 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<ItemStack> 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<ItemStack> 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<ItemStack> 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 5878cd5ee450430276d0d97f2a33d49109d09ed5..fffaad68dd29198ae03994dcbd6c0c1e447135b5 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1553,7 +1553,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
// Paper end - Fix MC-117075 use removeAll
if (ticker.isRemoved()) {
toRemove.add(ticker); // Paper - Fix MC-117075 use removeAll
- } else if (tickBlockEntities && this.shouldTickBlocksAt(ticker.getPos())) {
+ } else if (tickBlockEntities && this.shouldTickBlockPosFilterNull(ticker.getPos())) { // Leaves - Lithium Sleeping Block Entity
ticker.tick();
// Paper start - rewrite chunk system
if ((++tickedEntities & 7) == 0) {
@@ -1631,6 +1631,27 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
return this.shouldTickBlocksAt(ChunkPos.pack(pos));
}
+ // Leaves start - Lithium Sleeping Block Entity
+ public @org.jspecify.annotations.Nullable BlockEntity lithium$getLoadedExistingBlockEntity(BlockPos pos) {
+ if (!this.isOutsideBuildHeight(pos)) {
+ if (this.isClientSide || ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this, pos)) {
+ 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
+
public void explode(
final @Nullable Entity source, final double x, final double y, final double z, final float r, final Level.ExplosionInteraction blockInteraction
) {
diff --git a/net/minecraft/world/level/block/ComposterBlock.java b/net/minecraft/world/level/block/ComposterBlock.java
index 69e3969242ad52168e947af209e6fa72f36178ae..ad0012256b6e7c490271f36b390db96c4bfe8f3d 100644
--- a/net/minecraft/world/level/block/ComposterBlock.java
+++ b/net/minecraft/world/level/block/ComposterBlock.java
@@ -426,7 +426,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
@@ -448,7 +448,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;
@@ -494,12 +494,13 @@ public class ComposterBlock extends Block implements WorldlyContainerHolder {
}
// Paper end - Add CompostItemEvent and EntityCompostItemEvent
this.level.levelEvent(LevelEvent.COMPOSTER_FILL, this.pos, newState != this.state ? 1 : 0);
+ if (io.nanachiyo0721.shiroha.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 2339099e05af80b234e64778822b012cc6318157..a98a9ed2f992b7f269e6c9d893966dd1a687f43e 100644
--- a/net/minecraft/world/level/block/DiodeBlock.java
+++ b/net/minecraft/world/level/block/DiodeBlock.java
@@ -179,6 +179,7 @@ public abstract class DiodeBlock extends HorizontalDirectionalBlock {
@Override
protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) {
this.updateNeighborsInFront(level, pos, state);
+ if (io.nanachiyo0721.shiroha.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 3932da81757ea0ab7a41617165f5b64dc44ea3eb..ef6aef74c8b031b1c05fc393e4a36c00037e017a 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.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jspecify.annotations.Nullable;
-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<HopperBlock> CODEC = simpleCodec(HopperBlock::new);
public static final EnumProperty<Direction> FACING = BlockStateProperties.FACING_HOPPER;
public static final BooleanProperty ENABLED = BlockStateProperties.ENABLED;
@@ -100,6 +100,17 @@ public class HopperBlock extends BaseEntityBlock {
protected void onPlace(final BlockState state, final Level level, final BlockPos pos, final BlockState oldState, final boolean movedByPiston) {
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 (io.nanachiyo0721.shiroha.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
}
}
@@ -118,6 +129,7 @@ public class HopperBlock extends BaseEntityBlock {
protected void neighborChanged(
final BlockState state, final Level level, final BlockPos pos, final Block block, final @Nullable Orientation orientation, final boolean movedByPiston
) {
+ if (io.nanachiyo0721.shiroha.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
this.checkPoweredState(level, pos, state);
}
@@ -177,4 +189,25 @@ public class HopperBlock extends BaseEntityBlock {
protected boolean isPathfindable(final BlockState state, final PathComputationType type) {
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 78dbdf43ac9095eb3cf0b0653bab2603d5527ff4..f1c5f122102e3164092342239e866ed672a1ec2e 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.ValueOutput;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
-public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, StackedContentsCompatible, RecipeCraftingHolder {
+public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, StackedContentsCompatible, RecipeCraftingHolder, 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;
@@ -162,6 +162,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 (io.nanachiyo0721.shiroha.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 (changed) {
setChanged(level, pos, state);
}
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.checkSleep(state); // Leaves - Lithium Sleeping Block Entity
}
private static void consumeFuel(final NonNullList<ItemStack> items, final ItemStack fuel) {
@@ -510,4 +512,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.litTimeRemaining <= 0 && this.cookingTimer == 0 && (state.is(net.minecraft.world.level.block.Blocks.FURNACE) || state.is(net.minecraft.world.level.block.Blocks.BLAST_FURNACE) || state.is(net.minecraft.world.level.block.Blocks.SMOKER)) && this.level != null) { // Leaves - Paper 26.1 fix
+ 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<net.minecraft.world.item.ItemStack> getInventoryLithium() {
+ return items;
+ }
+
+ @Override
+ public void setInventoryLithium(net.minecraft.core.NonNullList<net.minecraft.world.item.ItemStack> 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 118af4e35eec652e7eb8a484a44652cfabc1bab5..35498b936f7d3f82a3379a2e0e3da2060d8d4bb2 100644
--- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
@@ -23,7 +23,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
-public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
+public class BarrelBlockEntity extends RandomizableContainerBlockEntity implements 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<org.bukkit.entity.HumanEntity> transaction = new java.util.ArrayList<>();
private int maxStack = MAX_STACK;
@@ -129,6 +129,7 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
@Override
protected void setItems(final NonNullList<ItemStack> items) {
this.items = items;
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity
}
@Override
@@ -180,4 +181,18 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
double z = this.worldPosition.getZ() + 0.5 + direction.getZ() / 2.0;
this.level.playSound(null, x, y, z, event, SoundSource.BLOCKS, 0.5F, this.level.getRandom().nextFloat() * 0.1F + 0.9F);
}
+
+ // Leaves start - Lithium Sleeping Block Entity
+
+
+ @Override
+ public net.minecraft.core.NonNullList<net.minecraft.world.item.ItemStack> getInventoryLithium() {
+ return items;
+ }
+
+ @Override
+ public void setInventoryLithium(net.minecraft.core.NonNullList<net.minecraft.world.item.ItemStack> 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 0e594121b60aa5a01b5bbe94b80350f2fc698b1a..1b6965f8ba4b9376fdb1c72ff8966006f9eb59cd 100644
--- a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
@@ -25,8 +25,17 @@ import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
+// 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 { // Leaves - Lithium Sleeping Block Entity
public LockCode lockKey = LockCode.NO_LOCK;
public @Nullable Component name;
@@ -39,6 +48,7 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co
super.loadAdditional(input);
this.lockKey = LockCode.fromTag(input);
this.name = parseCustomNameSafe(input, "CustomName");
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this instanceof InventoryChangeTracker inventoryChangeTracker) inventoryChangeTracker.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity
}
@Override
@@ -183,4 +193,98 @@ 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<InventoryChangeListener> inventoryChangeListeners = null;
+ ReferenceArraySet<InventoryChangeListener> inventoryHandlingTypeListeners = null;
+
+ @Override
+ public void lithium$emitContentModified() {
+ ReferenceArraySet<InventoryChangeListener> inventoryChangeListeners = this.inventoryChangeListeners;
+ if (inventoryChangeListeners != null) {
+ for (InventoryChangeListener inventoryChangeListener : inventoryChangeListeners) {
+ inventoryChangeListener.lithium$handleInventoryContentModified(this);
+ }
+ inventoryChangeListeners.clear();
+ }
+ }
+
+ @Override
+ public void lithium$emitStackListReplaced() {
+ ReferenceArraySet<InventoryChangeListener> 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<InventoryChangeListener> 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<InventoryChangeListener> 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);
+ }
+ }
+ // Leaves end - Lithium Sleeping Block Entity
}
diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java
index 2887cbaa954771d0fe357d8ec8df75073e9afa91..e3cbfcce259e3b6b5f8dae28c67be406c2650bdc 100644
--- a/net/minecraft/world/level/block/entity/BlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BlockEntity.java
@@ -37,8 +37,16 @@ import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
-
-public abstract class BlockEntity implements DebugValueSource, TypedInstance<BlockEntityType<?>> {
+// 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 DebugValueSource, TypedInstance<BlockEntityType<?>>, ComparatorTracker, SetBlockStateHandlingBlockEntity, SetChangedHandlingBlockEntity { // Leaves - Lithium Sleeping Block Entity
static final ThreadLocal<Boolean> IGNORE_TILE_UPDATES = ThreadLocal.withInitial(() -> Boolean.FALSE); // Paper - Perf: Optimize Hoppers // Folia - region threading
// CraftBukkit start - data containers
private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
@@ -65,6 +73,7 @@ public abstract class BlockEntity implements DebugValueSource, TypedInstance<Blo
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(final BlockState blockState) {
@@ -218,6 +227,7 @@ public abstract class BlockEntity implements DebugValueSource, TypedInstance<Blo
if (this.level != null) {
if (IGNORE_TILE_UPDATES.get().booleanValue()) return; // Paper - Perf: Optimize Hoppers // Folia - region threading
setChanged(this.level, this.worldPosition, this.blockState);
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) lithium$handleSetChanged(); // Leaves - Lithium Sleeping Block Entity
}
}
@@ -249,7 +259,9 @@ public abstract class BlockEntity implements DebugValueSource, TypedInstance<Blo
}
public void setRemoved() {
+ this.hasComparators = UNKNOWN; // Leaves - Lithium Sleeping Block Entity
this.remove = true;
+ if (io.nanachiyo0721.shiroha.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() {
@@ -294,6 +306,7 @@ public abstract class BlockEntity implements DebugValueSource, TypedInstance<Blo
public void setBlockState(final BlockState blockState) {
this.validateBlockState(blockState);
this.blockState = blockState;
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$handleSetBlockState(); // Leaves - Lithium Sleeping Block Entity
}
protected void applyImplicitComponents(final DataComponentGetter components) {
@@ -396,4 +409,32 @@ public abstract class BlockEntity implements DebugValueSource, TypedInstance<Blo
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 5d26efb1feb1fd7d0f3bcc0fd7678b303518312a..a2fa0fa4658290ac49c01d5b66cbfbdcdae8f50f 100644
--- a/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
@@ -27,7 +27,7 @@ import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import org.jspecify.annotations.Nullable;
-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};
@@ -139,6 +139,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
}
public static void serverTick(final Level level, final BlockPos pos, final BlockState selfState, final BrewingStandBlockEntity entity) {
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.checkSleep(selfState); // Leaves - Lithium Sleeping Block Entity
ItemStack fuel = entity.items.get(4);
if (entity.fuel <= 0 && fuel.is(ItemTags.BREWING_FUEL)) {
// CraftBukkit start
@@ -156,6 +157,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
fuel.shrink(1);
}
// CraftBukkit end
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
setChanged(level, pos, selfState);
}
@@ -170,7 +172,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
} else if (!brewable || !ingredient.is(entity.ingredient)) {
entity.brewTime = 0;
}
-
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
setChanged(level, pos, selfState);
} else if (brewable && entity.fuel > 0) {
entity.fuel--;
@@ -183,6 +185,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
entity.brewTime = event.getBrewingTime(); // 400 -> event.getTotalBrewTime() // Paper - use brewing time from event
// CraftBukkit end
entity.ingredient = ingredient.getItem();
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
setChanged(level, pos, selfState);
}
@@ -291,6 +294,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
}
this.fuel = input.getByteOr("Fuel", (byte)0);
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.isSleeping() && this.level != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
}
@Override
@@ -337,4 +341,53 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
protected AbstractContainerMenu createMenu(final int containerId, final Inventory inventory) {
return new BrewingStandMenu(containerId, inventory, 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<net.minecraft.world.item.ItemStack> getInventoryLithium() {
+ return items;
+ }
+
+ @Override
+ public void setInventoryLithium(net.minecraft.core.NonNullList<net.minecraft.world.item.ItemStack> 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 64d0d60d9025de2b5eb16b31c6437372d4c20c5e..6d8841ffb610bb0d2c65ba51726cf9a5588128bb 100644
--- a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java
@@ -39,7 +39,7 @@ import net.minecraft.world.level.storage.ValueOutput;
import org.jspecify.annotations.Nullable;
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;
@@ -111,7 +111,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable {
if (changed) {
setChanged(level, pos, state);
- }
+ } else if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.lithium$startSleeping(); // Leaves - Lithium Sleeping Block Entity
}
public static void cooldownTick(final Level level, final BlockPos pos, final BlockState state, final CampfireBlockEntity entity) {
@@ -126,7 +126,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable {
if (changed) {
setChanged(level, pos, state);
- }
+ } else if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.lithium$startSleeping(); // Leaves - Lithium Sleeping Block Entity
}
public static void particleTick(final Level level, final BlockPos pos, final BlockState state, final CampfireBlockEntity entity) {
@@ -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 (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
}
@Override
@@ -234,6 +235,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable {
this.cookingTime[slot] = event.getTotalCookTime(); // recipe.get().value().cookingTime() -> event.getTotalCookTime()
// CraftBukkit end
this.cookingProgress[slot] = 0;
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
this.items.set(slot, placeItem.consumeAndReturn(1, sourceEntity));
serverLevel.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(sourceEntity, this.getBlockState()));
this.markUpdated();
@@ -277,4 +279,30 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable {
public void removeComponentsFromTag(final 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 a9273aebb5c8efe78e88158f11e1ee2af0dff3f1..8dca1ddd7740e86367ce16db56b75b649fa80b07 100644
--- a/net/minecraft/world/level/block/entity/ChestBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/ChestBlockEntity.java
@@ -25,7 +25,7 @@ import net.minecraft.world.level.block.state.properties.ChestType;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
-public class ChestBlockEntity extends RandomizableContainerBlockEntity implements LidBlockEntity {
+public class ChestBlockEntity extends RandomizableContainerBlockEntity implements LidBlockEntity, 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;
public static final Component DEFAULT_NAME = Component.translatable("container.chest");
private NonNullList<ItemStack> items = NonNullList.withSize(27, ItemStack.EMPTY);
@@ -140,6 +140,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
public static void lidAnimateTick(final Level level, final BlockPos pos, final BlockState state, final ChestBlockEntity entity) {
entity.chestLidController.tickLid();
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.checkSleep(); // Leaves - Lithium Sleeping Block Entity
}
public static void playSound(final Level level, final BlockPos worldPosition, final BlockState blockState, final SoundEvent event) {
@@ -161,6 +162,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
@Override
public boolean triggerEvent(final int b0, final int b1) {
if (b0 == EVENT_SET_OPEN_COUNT) {
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.sleepingTicker != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
this.chestLidController.shouldBeOpen(b1 > 0);
return true;
} else {
@@ -198,6 +200,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
@Override
protected void setItems(final NonNullList<ItemStack> items) {
this.items = items;
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity
}
@Override
@@ -233,4 +236,52 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement
Block block = blockState.getBlock();
level.blockEvent(pos, block, EVENT_SET_OPEN_COUNT, current);
}
+
+ // 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<net.minecraft.world.item.ItemStack> getInventoryLithium() {
+ return items;
+ }
+
+ @Override
+ public void setInventoryLithium(net.minecraft.core.NonNullList<net.minecraft.world.item.ItemStack> 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 8697f4d589766163a56b0de5be4fad999d09f809..19db4302ca6f1b3f2f86aef34f0d80229fb1ace8 100644
--- a/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/ChiseledBookShelfBlockEntity.java
@@ -22,7 +22,7 @@ import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import org.slf4j.Logger;
-public class ChiseledBookShelfBlockEntity extends BlockEntity implements ListBackedContainer {
+public class ChiseledBookShelfBlockEntity extends BlockEntity implements ListBackedContainer, 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;
@@ -171,4 +171,6 @@ public class ChiseledBookShelfBlockEntity extends BlockEntity implements ListBac
public void removeComponentsFromTag(final 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 02d86d0675de22546a225dd6eaac5875eba0ce54..ae33a37a67f39a7c35228c7f95e3b78900685e12 100644
--- a/net/minecraft/world/level/block/entity/CrafterBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/CrafterBlockEntity.java
@@ -23,7 +23,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
-public class CrafterBlockEntity extends RandomizableContainerBlockEntity implements CraftingContainer {
+public class CrafterBlockEntity extends RandomizableContainerBlockEntity implements CraftingContainer, org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity, org.leavesmc.leaves.lithium.common.block.entity.SetChangedHandlingBlockEntity { // Leaves - Lithium Sleeping Block Entity
public static final int CONTAINER_WIDTH = 3;
public static final int CONTAINER_HEIGHT = 3;
public static final int CONTAINER_SIZE = 9;
@@ -171,6 +171,7 @@ public class CrafterBlockEntity extends RandomizableContainerBlockEntity impleme
}
});
this.containerData.set(9, input.getIntOr("triggered", 0));
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.isSleeping() && this.level != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
}
@Override
@@ -280,10 +281,12 @@ public class CrafterBlockEntity extends RandomizableContainerBlockEntity impleme
level.setBlock(blockPos, blockState.setValue(CrafterBlock.CRAFTING, false), Block.UPDATE_ALL);
}
}
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && craftingTicksRemaining < 0) entity.checkSleep(); // Leaves - Lithium Sleeping Block Entity
}
public void setCraftingTicksRemaining(final int maxCraftingTicks) {
this.craftingTicksRemaining = maxCraftingTicks;
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.isSleeping() && this.level != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
}
public int getRedstoneSignal() {
@@ -302,4 +305,43 @@ public class CrafterBlockEntity extends RandomizableContainerBlockEntity impleme
private boolean slotCanBeDisabled(final int slotId) {
return slotId > -1 && slotId < 9 && this.items.get(slotId).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 736f5fd76f4a86ab16740904b5c0823413d65cd2..b1a063c3e75cfcd99e936f1e336ce5464a8cc3b7 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 static final Component DEFAULT_NAME = Component.translatable("container.dispenser");
private NonNullList<ItemStack> items = NonNullList.withSize(9, ItemStack.EMPTY);
@@ -135,10 +135,23 @@ public class DispenserBlockEntity extends RandomizableContainerBlockEntity {
@Override
protected void setItems(final NonNullList<ItemStack> items) {
this.items = items;
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity
}
@Override
protected AbstractContainerMenu createMenu(final int containerId, final Inventory inventory) {
return new DispenserMenu(containerId, inventory, this);
}
+
+ // Leaves start - Lithium Sleeping Block Entity
+ @Override
+ public net.minecraft.core.NonNullList<net.minecraft.world.item.ItemStack> getInventoryLithium() {
+ return items;
+ }
+
+ @Override
+ public void setInventoryLithium(net.minecraft.core.NonNullList<net.minecraft.world.item.ItemStack> 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 6109d8da984b398bb1eb6cd6ab9a55f0511a615c..d3d6b3aeb24da7f3bf92c327095a9dc9494c92da 100644
--- a/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/EnderChestBlockEntity.java
@@ -11,7 +11,7 @@ import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ChestBlock;
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() {
// Paper start - delay open/close callbacks
@@ -66,11 +66,13 @@ public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity
public static void lidAnimateTick(final Level level, final BlockPos pos, final BlockState state, final EnderChestBlockEntity entity) {
entity.chestLidController.tickLid();
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.checkSleep(); // Leaves - Lithium Sleeping Block Entity
}
@Override
public boolean triggerEvent(final int b0, final int b1) {
if (b0 == ChestBlock.EVENT_SET_OPEN_COUNT) {
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.sleepingTicker != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
this.chestLidController.shouldBeOpen(b1 > 0);
return true;
} else {
@@ -107,4 +109,36 @@ public class EnderChestBlockEntity extends BlockEntity implements LidBlockEntity
public float getOpenNess(final float a) {
return this.chestLidController.getOpenness(a);
}
+
+ // 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 4c9d0bcd8820b250b51c06fff4531021ab57d408..fb0f4edfa0c6d08364db5a62b13e98a3d32f0d42 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.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.phys.AABB;
import org.jspecify.annotations.Nullable;
-
-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][];
@@ -77,9 +98,19 @@ 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) { // Shiroha - Region threading for lithium sleeping block entity
this.tickedGameTime += fromRedstoneTimeOffset;
}
+ // Shiroha start - Region threading for lithium sleeping block entity
+ if (this.insertInventoryEntityFailedSearchTime != Long.MIN_VALUE)
+ this.insertInventoryEntityFailedSearchTime += fromRedstoneTimeOffset;
+
+ if (this.extractInventoryEntityFailedSearchTime != Long.MIN_VALUE)
+ this.extractInventoryEntityFailedSearchTime += fromRedstoneTimeOffset;
+
+ if (this.collectItemEntityAttemptTime != Long.MIN_VALUE)
+ this.collectItemEntityAttemptTime += fromRedstoneTimeOffset;
+ // Shiroha end
}
// Folia end - region threading
@@ -129,6 +160,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@Override
public void setBlockState(final BlockState blockState) {
+ if (io.nanachiyo0721.shiroha.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);
}
@@ -145,6 +177,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
entity.setCooldown(0);
// Spigot start
boolean result = tryMoveItems(level, pos, state, entity, () -> suckInItems(level, entity));
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) entity.checkSleepingConditions(); // Leaves - Lithium Sleeping Block Entity
if (!result && entity.level.spigotConfig.hopperCheck > 1) {
entity.setCooldown(entity.level.spigotConfig.hopperCheck);
}
@@ -210,6 +243,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (changed) {
entity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot
setChanged(level, pos, state);
+ // Leaves start - Lithium Sleeping Block Entity
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled
+ && !entity.isOnCooldown()
+ && !entity.isSleeping()
+ && !state.getValue(HopperBlock.ENABLED)) {
+ entity.lithium$startSleeping();
+ }
+ // Leaves end - Lithium Sleeping Block Entity
return true;
}
}
@@ -406,6 +447,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
}
}
@@ -449,12 +491,20 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// Paper end - Perf: Optimize Hoppers
private static boolean ejectItems(final Level level, final BlockPos blockPos, final HopperBlockEntity self) {
- Container container = getAttachedContainer(level, blockPos, self);
+ Container container = io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled ? self.getInsertInventory(level) : getAttachedContainer(level, blockPos, self); // Leaves - Lithium Sleeping Block Entity
if (container == null) {
return false;
}
Direction direction = self.facing.getOpposite();
+ // Leaves start - Lithium Sleeping Block Entity
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) {
+ Boolean res = lithiumInsert(level, blockPos, self, container);
+ if (res != null) {
+ return res;
+ }
+ }
+ // Leaves end - Lithium Sleeping Block Entity
if (isFullContainer(container, direction)) {
return false;
}
@@ -510,13 +560,21 @@ 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 container = getSourceContainer(level, hopper, blockPos, blockState);
- if (container != null) {
+ Container sourceContainer = io.nanachiyo0721.shiroha.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 (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) {
+ Boolean res = lithiumExtract(level, hopper, sourceContainer);
+ if (res != null) {
+ return res;
+ }
+ }
+ // Leaves end - Lithium Sleeping Block Entity
- for (int slot : getSlots(container, direction)) {
- if (tryTakeInItemFromSlot(hopper, container, slot, direction, level)) { // Spigot
+ for (int slot : getSlots(sourceContainer, direction)) {
+ if (tryTakeInItemFromSlot(hopper, sourceContainer, slot, direction, level)) { // Spigot
return true;
}
}
@@ -527,7 +585,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
&& blockState.isCollisionShapeFullBlock(level, blockPos)
&& !blockState.is(BlockTags.DOES_NOT_BLOCK_HOPPERS);
if (!isBlocked) {
- for (ItemEntity entity : getItemsAtAndAbove(level, hopper)) {
+ for (ItemEntity entity : io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled ? lithiumGetInputItemEntities(level, hopper) : getItemsAtAndAbove(level, hopper)) { // Leaves - Lithium Sleeping Block Entity
if (addItem(hopper, entity)) {
return true;
}
@@ -649,7 +707,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// CraftBukkit start
private static @Nullable Container runHopperInventorySearchEvent(
- Container container,
+ @Nullable Container container, // Leaves - Lithium Sleeping Block Entity
org.bukkit.craftbukkit.block.CraftBlock hopper,
org.bukkit.craftbukkit.block.CraftBlock searchLocation,
org.bukkit.event.inventory.HopperInventorySearchEvent.ContainerType containerType
@@ -775,6 +833,19 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
public void setCooldown(final int time) {
+ // Leaves start - Lithium Sleeping Block Entity
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) {
+ if (time == 7) {
+ if (this.tickedGameTime == Long.MAX_VALUE) {
+ this.sleepOnlyCurrentTick();
+ } else {
+ this.wakeUpNow();
+ }
+ } else if (time > 0 && this.sleepingTicker != null) {
+ this.wakeUpNow();
+ }
+ }
+ // Leaves end - Lithium Sleeping Block Entity
this.cooldownTime = time;
}
@@ -794,6 +865,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@Override
protected void setItems(final NonNullList<ItemStack> items) {
this.items = items;
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity
}
public static void entityInside(final Level level, final BlockPos pos, final BlockState blockState, final Entity entity, final HopperBlockEntity hopper) {
@@ -808,4 +880,758 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
protected AbstractContainerMenu createMenu(final int containerId, final Inventory inventory) {
return new HopperMenu(containerId, inventory, this);
}
+
+ // Leaves start - Lithium Sleeping Block Entity
+ private LevelChunk.@org.jspecify.annotations.Nullable RebindableTickingBlockEntityWrapper tickWrapper = null; // Leaves - Paper 26.1 fix
+ @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<ChunkSectionItemEntityMovementTracker> collectItemEntityTracker;
+ private boolean collectItemEntityTrackerWasEmpty;
+ @Nullable
+ private AABB collectItemEntityBox;
+ private long collectItemEntityAttemptTime;
+
+ @Nullable
+ private List<ChunkSectionInventoryEntityTracker> extractInventoryEntityTracker;
+ @Nullable
+ private AABB extractInventoryEntityBox;
+ private long extractInventoryEntityFailedSearchTime;
+
+ @Nullable
+ private List<ChunkSectionInventoryEntityTracker> 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 LevelChunk.@org.jspecify.annotations.Nullable RebindableTickingBlockEntityWrapper lithium$getTickWrapper() { // Leaves - Paper 26.1 fix
+ 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.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<ItemStack> getInventoryLithium() {
+ return items;
+ }
+
+ @Override
+ public void setInventoryLithium(NonNullList<ItemStack> 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<Container> 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.getRandom().nextInt(inventoryEntities.size())); // Leaves - Paper 26.1 fix
+ 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<Container> 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.getRandom().nextInt(inventoryEntities.size())); // Leaves - Paper 26.1 fix
+ 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<ItemEntity> 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<ItemEntity> 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 worldData = world.getCurrentWorldData();
+ worldData.skipPushModeEventFire = worldData.skipHopperEvents;
+ //noinspection ConstantConditions
+ if (!(hopperBlockEntity.insertInventory == insertInventory && hopperBlockEntity.insertStackList.getFullSlots() == hopperBlockEntity.insertStackList.size())) {
+ Direction fromDirection = hopperBlockEntity.facing.getOpposite();
+ int size = hopperStackList.size();
+ for (int i = 0; i < size; ++i) {
+ ItemStack transferStack = hopperStackList.get(i);
+ if (!transferStack.isEmpty()) {
+ if (!worldData.skipPushModeEventFire && canTakeItemFromContainer(insertInventory, hopperBlockEntity, transferStack, i, Direction.DOWN)) {
+ 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;
+ }
+ }
+
+ 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 (!world.getCurrentWorldData().skipPullModeEventFire) {
+ itemStack = callPullMoveEvent(to, from, itemStack);
+ if (itemStack == null) { // cancelled
+ return true;
+ }
+ }
+ //calling removeStack is necessary due to its side effects (markDirty in LootableContainerBlockEntity)
+ ItemStack takenItem = from.removeItem(fromSlot, 1);
+ assert !takenItem.isEmpty();
+ boolean transferSuccess = 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 174e39fb109bef3dd27ebc14aa13f6faabb10547..14138b6a4ab15e372f185171c7972bff353c2ae1 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.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.Nullable;
-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, blockState);
}
}
+ if (io.nanachiyo0721.shiroha.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(final int b0, final int b1) {
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && this.sleepingTicker != null) this.wakeUpNow(); // Leaves - Lithium Sleeping Block Entity
if (b0 == EVENT_SET_OPEN_COUNT) {
this.openCount = b1;
if (b1 == 0) {
@@ -269,6 +271,7 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl
@Override
protected void setItems(final NonNullList<ItemStack> items) {
this.itemStacks = items;
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$emitStackListReplaced(); // Leaves - Lithium Sleeping Block Entity
}
@Override
@@ -309,4 +312,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<net.minecraft.world.item.ItemStack> getInventoryLithium() {
+ return itemStacks;
+ }
+
+ @Override
+ public void setInventoryLithium(net.minecraft.core.NonNullList<net.minecraft.world.item.ItemStack> 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..e50a892ebc85a5d79e13ce9a4578cbd2e298d1fd 100644
--- a/net/minecraft/world/level/block/entity/TickingBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/TickingBlockEntity.java
@@ -12,4 +12,8 @@ public interface TickingBlockEntity {
String getType();
BlockEntity getTileEntity(); // Folia - region threading
+
+ // Shiroha start - Region threading for lithium sleeping block entity
+ default void updateTicksForLithium(long redstoneGameTimeOffset) {}
+ // Shiroha end
}
diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java
index c78d77089e34c4bb559f173b449bfb7d91b05d5c..2cae0d5c815b5c273267e554c32ce117b608eebf 100644
--- a/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -83,7 +83,7 @@ import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jspecify.annotations.Nullable;
-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
};
@@ -155,6 +155,7 @@ public abstract class BlockBehaviour implements FeatureElement {
final BlockState neighbourState,
final RandomSource random
) {
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) this.lithium$handleShapeUpdate(level, state, pos, neighbourPos, neighbourState); // 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/block/state/pattern/BlockPattern.java b/net/minecraft/world/level/block/state/pattern/BlockPattern.java
index ed5a5fb38bfe8fb9216c7eab00b833485c4bbade..fc45703525901d700c98b5ad0c6eb10b65e775d8 100644
--- a/net/minecraft/world/level/block/state/pattern/BlockPattern.java
+++ b/net/minecraft/world/level/block/state/pattern/BlockPattern.java
@@ -57,7 +57,7 @@ public class BlockPattern {
return this.matches(origin, forwards, up, cache);
}
- private BlockPattern.@Nullable BlockPatternMatch matches(
+ public BlockPattern.@Nullable BlockPatternMatch matches( // Leaves - private -> public
final BlockPos origin, final Direction forwards, final Direction up, final LoadingCache<BlockPos, BlockInWorld> cache
) {
for (int x = 0; x < this.width; x++) {
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 0e84b695a677e71f443a29c78fc5025eec7ae7f4..df6d65c258ea48e4e3b2f4ec02150ab06cd6682f 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -622,6 +622,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
LevelChunk.RebindableTickingBlockEntityWrapper ticker = this.tickersInLevel.remove(pos);
if (ticker != null) {
ticker.rebind(NULL_TICKER);
+ ticker.createdBy = null; // Shiroha - Region threading for sleeping block entity
}
}
@@ -883,6 +884,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
this.blockEntities.values().forEach(BlockEntity::setRemoved);
this.blockEntities.clear();
this.tickersInLevel.values().forEach(ticker -> ticker.rebind(NULL_TICKER));
+ this.tickersInLevel.values().forEach(ticker -> ticker.createdBy = null); // Shiroha - Region threading for sleeping block entity
this.tickersInLevel.clear();
}
@@ -916,10 +918,14 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
this.tickersInLevel.compute(blockEntity.getBlockPos(), (blockPos, existingTicker) -> {
TickingBlockEntity actualTicker = this.createTicker(blockEntity, ticker);
if (existingTicker != null) {
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && blockEntity instanceof org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity sleepingBlockEntity) sleepingBlockEntity.lithium$setTickWrapper(existingTicker); // Leaves - Lithium Sleeping Block Entity
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) existingTicker.createdBy = blockEntity; // Shiroha - Region threading for lithium sleeping block entity
existingTicker.rebind(actualTicker);
return (LevelChunk.RebindableTickingBlockEntityWrapper)existingTicker;
} else if (this.isInLevel()) {
LevelChunk.RebindableTickingBlockEntityWrapper result = new LevelChunk.RebindableTickingBlockEntityWrapper(actualTicker);
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled && blockEntity instanceof org.leavesmc.leaves.lithium.common.block.entity.SleepingBlockEntity sleepingBlockEntity) sleepingBlockEntity.lithium$setTickWrapper(result); // Leaves - Lithium Sleeping Block Entity
+ if (io.nanachiyo0721.shiroha.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled) result.createdBy = blockEntity; // Shiroha - Region threading for lithium sleeping block entity
this.level.addBlockEntityTicker(result);
return result;
} else {
@@ -1028,20 +1034,36 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
void run(LevelChunk levelChunk);
}
- private 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 BlockEntity createdBy; // Shiroha - Region threading for lithium sleeping block entity
private RebindableTickingBlockEntityWrapper(final TickingBlockEntity ticker) {
this.ticker = ticker;
}
- private void rebind(final TickingBlockEntity ticker) {
+ public void rebind(final TickingBlockEntity ticker) { // Leaves - default -> public
this.ticker = ticker;
}
// Folia start - region threading
@Override
public BlockEntity getTileEntity() {
+ // Shiroha start - Region threading for lithium sleeping block entity
+ if (true) {
+ if (this.ticker != null) {
+ var ret = this.ticker.getTileEntity();
+
+ if (ret == null) {
+ return this.createdBy;
+ }
+
+ return ret;
+ }
+
+ return null;
+ }
+ // Shiroha end
return this.ticker == null ? null : this.ticker.getTileEntity();
}
// Folia end - region threading
@@ -1070,6 +1092,16 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
public String toString() {
return this.ticker + " <wrapped>";
}
+
+ // Shiroha start - Region threading for lithium sleeping block entity
+ @Override
+ public void updateTicksForLithium(long redstoneGameTimeOffset) {
+ if (this.ticker != null) {
+ this.ticker.updateTicksForLithium(redstoneGameTimeOffset);
+ }
+ }
+
+ // Shiroha end
}
@FunctionalInterface