diff --git a/build-data/divinemc.at b/build-data/divinemc.at index 73ecc0d..f02ebc5 100644 --- a/build-data/divinemc.at +++ b/build-data/divinemc.at @@ -14,6 +14,9 @@ public net.minecraft.world.entity.ai.sensing.Sensor scanRate public net.minecraft.world.entity.ai.sensing.Sensor timeToTick public net.minecraft.world.level.ServerExplosion damageSource public net.minecraft.world.level.ServerExplosion source +public net.minecraft.world.level.chunk.LevelChunk$RebindableTickingBlockEntityWrapper +public net.minecraft.world.level.chunk.LevelChunk$RebindableTickingBlockEntityWrapper rebind(Lnet/minecraft/world/level/block/entity/TickingBlockEntity;)V +public net.minecraft.world.level.chunk.LevelChunk$RebindableTickingBlockEntityWrapper ticker public net.minecraft.world.level.chunk.LevelChunkSection nonEmptyBlockCount public net.minecraft.world.level.chunk.LevelChunkSection tickingBlockCount public net.minecraft.world.level.chunk.LevelChunkSection tickingFluidCount diff --git a/divinemc-server/minecraft-patches/features/0042-lithium-block_entity_ticking.sleeping.patch b/divinemc-server/minecraft-patches/features/0042-lithium-block_entity_ticking.sleeping.patch new file mode 100644 index 0000000..44b6084 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0042-lithium-block_entity_ticking.sleeping.patch @@ -0,0 +1,565 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Thu, 6 Mar 2025 00:09:54 +0300 +Subject: [PATCH] lithium: block_entity_ticking.sleeping + + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index c3163e9f79fcf62e7d7a75d8f27695e232dc7e8c..12bb73f65872d68e5ec4f37ecd8e0122c1fd721f 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -2439,7 +2439,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + CsvOutput csvOutput = CsvOutput.builder().addColumn("x").addColumn("y").addColumn("z").addColumn("type").build(output); + + for (TickingBlockEntity tickingBlockEntity : this.blockEntityTickers) { +- BlockPos pos = tickingBlockEntity.getPos(); ++ BlockPos pos = getPosOrOrigin(tickingBlockEntity); // DivineMC - lithium: block_entity_ticking.sleeping + csvOutput.writeRow(pos.getX(), pos.getY(), pos.getZ(), tickingBlockEntity.getType()); + } + } +@@ -2494,6 +2494,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + return this; + } + ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ private BlockPos getPosOrOrigin(TickingBlockEntity instance) { ++ BlockPos pos = instance.getPos(); ++ if (pos == null) { ++ return BlockPos.ZERO; ++ } ++ return pos; ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping ++ + @VisibleForTesting + public String getWatchdogStats() { + return String.format( +diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java +index 7c80f90ccab0f90a4c76c2d195f4aaa0b12deb9a..9f5a219e2fae7bb08ab921d13d9ae9e782d2afdb 100644 +--- a/net/minecraft/world/level/Level.java ++++ b/net/minecraft/world/level/Level.java +@@ -1535,7 +1535,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + // Spigot end + if (tickingBlockEntity.isRemoved()) { + this.blockEntityTickers.markAsRemoved(this.tileTickPosition); // DivineMC - optimize block entity removals - Fix MC-117075 +- } else if (runsNormally && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) { ++ } else if (runsNormally && this.shouldTickBlockPosFilterNull(this, tickingBlockEntity.getPos())) { // DivineMC - lithium: block_entity_ticking.sleeping + tickingBlockEntity.tick(); + // Paper start - rewrite chunk system + if ((++tickedEntities & 7) == 0) { +@@ -1552,6 +1552,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + this.spigotConfig.currentPrimedTnt = 0; // Spigot + } + ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ private boolean shouldTickBlockPosFilterNull(Level instance, BlockPos pos) { ++ if (pos == null) { ++ return false; ++ } ++ return instance.shouldTickBlocksAt(pos); ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping ++ + public void guardEntityTick(Consumer consumerEntity, T entity) { + try { + consumerEntity.accept(entity); +diff --git a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java +index 947b2911a71768c43180ca8dcaaacd6ce93b3f55..c8c56a4a8872d4b2a437506322f97ccdc5a9dc87 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.block.Blocks; + import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.phys.Vec3; + +-public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, RecipeCraftingHolder, StackedContentsCompatible { ++public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, RecipeCraftingHolder, StackedContentsCompatible, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity { // DivineMC - lithium: block_entity_ticking.sleeping + protected static final int SLOT_INPUT = 0; + protected static final int SLOT_FUEL = 1; + protected static final int SLOT_RESULT = 2; +@@ -101,6 +101,38 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit + private final RecipeManager.CachedCheck quickCheck; + public final RecipeType recipeType; // Paper - cook speed multiplier API + public double cookSpeedMultiplier = 1.0; // Paper - cook speed multiplier API ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ 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; ++ } ++ ++ @Override ++ public void lithium$handleSetChanged() { ++ if (this.isSleeping() && this.level != null && !this.level.isClientSide) { ++ this.wakeUpNow(); ++ } ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + + protected AbstractFurnaceBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState, RecipeType recipeType) { + super(type, pos, blockState); +@@ -167,6 +199,11 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit + this.cookSpeedMultiplier = tag.getDouble("Paper.CookSpeedMultiplier"); + } + // Paper end - cook speed multiplier API ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ if (this.isSleeping() && this.level != null && !this.level.isClientSide) { ++ this.wakeUpNow(); ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + } + + @Override +@@ -291,7 +328,16 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit + } + + if (usedLavaFromUnderneath) furnace.items.set(1, ItemStack.EMPTY); // Purpur - Furnace uses lava from underneath ++ furnace.checkSleep(state); // DivineMC - lithium: block_entity_ticking.sleeping ++ } ++ ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ private void checkSleep(BlockState state) { ++ if (!this.isLit() && this.cookingTimer == 0 && (state.is(Blocks.FURNACE) || state.is(Blocks.BLAST_FURNACE) || state.is(Blocks.SMOKER)) && this.level != null) { ++ this.lithium$startSleeping(); ++ } + } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + + private static boolean canBurn( + RegistryAccess registryAccess, +diff --git a/net/minecraft/world/level/block/entity/BlockEntity.java b/net/minecraft/world/level/block/entity/BlockEntity.java +index 3fd0f42618e5c2c683335d1d3e0bb74c6d32ef66..5e0dbe39c8df0147a067b775455f3b73b2906363 100644 +--- a/net/minecraft/world/level/block/entity/BlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BlockEntity.java +@@ -39,6 +39,7 @@ public abstract class BlockEntity { + protected boolean remove; + private BlockState blockState; + private DataComponentMap components = DataComponentMap.EMPTY; ++ public void lithium$handleSetChanged() { } // DivineMC - lithium: block_entity_ticking.sleeping + + public BlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + this.type = type; +diff --git a/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java b/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java +index 94f9477e78600eded6eecc4c961576501001d187..3c82874cc490bc8e29bb44abcc4b0893cfebf00b 100644 +--- a/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java +@@ -24,7 +24,7 @@ import net.minecraft.world.level.Level; + import net.minecraft.world.level.block.BrewingStandBlock; + import net.minecraft.world.level.block.state.BlockState; + +-public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer { ++public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements WorldlyContainer, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity { // DivineMC - lithium: block_entity_ticking.sleeping + private static final int INGREDIENT_SLOT = 3; + private static final int FUEL_SLOT = 4; + private static final int[] SLOTS_FOR_UP = new int[]{3}; +@@ -40,6 +40,44 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements + private boolean[] lastPotionCount; + private Item ingredient; + public int fuel; ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ 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.level.isClientSide) { ++ this.wakeUpNow(); ++ } ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + protected final ContainerData dataAccess = new ContainerData() { + @Override + public int get(int index) { +@@ -128,6 +166,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements + } + + public static void serverTick(Level level, BlockPos pos, BlockState state, BrewingStandBlockEntity blockEntity) { ++ blockEntity.checkSleep(state); // DivineMC - lithium: block_entity_ticking.sleeping + ItemStack itemStack = blockEntity.items.get(4); + if (blockEntity.fuel <= 0 && itemStack.is(ItemTags.BREWING_FUEL)) { + // CraftBukkit start +@@ -145,6 +184,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements + itemStack.shrink(1); + } + // CraftBukkit end ++ blockEntity.wakeUpNow(); // DivineMC - lithium: block_entity_ticking.sleeping + setChanged(level, pos, state); + } + +@@ -160,6 +200,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements + blockEntity.brewTime = 0; + } + ++ blockEntity.wakeUpNow(); // DivineMC - lithium: block_entity_ticking.sleeping + setChanged(level, pos, state); + } else if (isBrewable && blockEntity.fuel > 0) { + blockEntity.fuel--; +@@ -172,6 +213,7 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements + blockEntity.brewTime = event.getBrewingTime(); // 400 -> event.getTotalBrewTime() // Paper - use brewing time from event + // CraftBukkit end + blockEntity.ingredient = itemStack1.getItem(); ++ blockEntity.wakeUpNow(); // DivineMC - lithium: block_entity_ticking.sleeping + setChanged(level, pos, state); + } + +@@ -278,6 +320,11 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements + } + + this.fuel = tag.getByte("Fuel"); ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ if (this.isSleeping() && this.level != null && !this.level.isClientSide()) { ++ this.wakeUpNow(); ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + } + + @Override +diff --git a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java +index 2811ecb7624877b6b04896811f1d1626d716dad6..dcf92ac304c0d54c8371ce63eeb4942a67e3796e 100644 +--- a/net/minecraft/world/level/block/entity/CampfireBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/CampfireBlockEntity.java +@@ -30,13 +30,50 @@ import net.minecraft.world.level.block.CampfireBlock; + import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.level.gameevent.GameEvent; + +-public class CampfireBlockEntity extends BlockEntity implements Clearable { ++public class CampfireBlockEntity extends BlockEntity implements Clearable, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity { // DivineMC - lithium: block_entity_ticking.sleeping + private static final int BURN_COOL_SPEED = 2; + private static final int NUM_SLOTS = 4; + private final NonNullList items = NonNullList.withSize(4, ItemStack.EMPTY); + public final int[] cookingProgress = new int[4]; + public final int[] cookingTime = new int[4]; + public final boolean[] stopCooking = new boolean[4]; // Paper - Add more Campfire API ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ 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 static void trySleepLit(CampfireBlockEntity campfireBlockEntity, boolean bl) { ++ if (!bl) { ++ campfireBlockEntity.lithium$startSleeping(); ++ } ++ } ++ ++ private static void trySleepUnlit(CampfireBlockEntity campfire, boolean hadProgress) { ++ if (!hadProgress) { ++ campfire.lithium$startSleeping(); ++ } ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + + public CampfireBlockEntity(BlockPos pos, BlockState blockState) { + super(BlockEntityType.CAMPFIRE, pos, blockState); +@@ -62,8 +99,14 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + SingleRecipeInput singleRecipeInput = new SingleRecipeInput(itemStack); + // Paper start - add recipe to cook events + final var optionalCookingRecipe = check.getRecipeFor(singleRecipeInput, level); ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ final boolean finalFlag = flag; + ItemStack itemStack1 = optionalCookingRecipe +- .map(recipe -> recipe.value().assemble(singleRecipeInput, level.registryAccess())) ++ .map(recipe -> { ++ trySleepLit(campfire, finalFlag); // Canvas ++ return recipe.value().assemble(singleRecipeInput, level.registryAccess()); ++ }) ++ // DivineMC end - lithium: block_entity_ticking.sleeping + .orElse(itemStack); + // Paper end - add recipe to cook events + if (itemStack1.isItemEnabled(level.enabledFeatures())) { +@@ -79,6 +122,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + ); + + if (!blockCookEvent.callEvent()) { ++ trySleepLit(campfire, flag); // DivineMC - lithium: block_entity_ticking.sleeping + return; + } + +@@ -104,6 +148,8 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + if (flag) { + setChanged(level, pos, state); + } ++ ++ trySleepLit(campfire, flag); // DivineMC - lithium: block_entity_ticking.sleeping + } + + public static void cooldownTick(Level level, BlockPos pos, BlockState state, CampfireBlockEntity blockEntity) { +@@ -119,6 +165,8 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + if (flag) { + setChanged(level, pos, state); + } ++ ++ trySleepUnlit(blockEntity, flag); // DivineMC - lithium: block_entity_ticking.sleeping + } + + public static void particleTick(Level level, BlockPos pos, BlockState state, CampfireBlockEntity blockEntity) { +@@ -175,6 +223,8 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + System.arraycopy(cookingState, 0, this.stopCooking, 0, Math.min(this.stopCooking.length, abyte.length)); + } + // Paper end - Add more Campfire API ++ ++ this.wakeUpNow(); // DivineMC - lithium: block_entity_ticking.sleeping + } + + @Override +@@ -224,6 +274,7 @@ public class CampfireBlockEntity extends BlockEntity implements Clearable { + this.cookingTime[i] = event.getTotalCookTime(); // i -> event.getTotalCookTime() + // CraftBukkit end + this.cookingProgress[i] = 0; ++ this.wakeUpNow(); // DivineMC - lithium: block_entity_ticking.sleeping + this.items.set(i, stack.consumeAndReturn(1, entity)); + level.gameEvent(GameEvent.BLOCK_CHANGE, this.getBlockPos(), GameEvent.Context.of(entity, this.getBlockState())); + this.markUpdated(); +diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java +index a0ee6ad6e7a6791605191d20d742e16cc9857a60..627af05bbfc3ed60fb22bfeb1b7c79c09cf4f442 100644 +--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java +@@ -28,7 +28,7 @@ import net.minecraft.world.level.block.HopperBlock; + import net.minecraft.world.level.block.state.BlockState; + import net.minecraft.world.phys.AABB; + +-public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper { ++public class HopperBlockEntity extends RandomizableContainerBlockEntity implements Hopper, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity { // DivineMC - lithium: block_entity_ticking.sleeping + 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][]; +@@ -36,6 +36,51 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + public int cooldownTime = -1; + private long tickedGameTime; + private Direction facing; ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ 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; ++ } ++ ++ @Override ++ public boolean lithium$startSleeping() { ++ if (this.isSleeping()) { ++ return false; ++ } ++ ++ net.minecraft.world.level.chunk.LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = this.lithium$getTickWrapper(); ++ if (tickWrapper != null) { ++ this.lithium$setSleepingTicker(tickWrapper.ticker); ++ tickWrapper.rebind(net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity.SLEEPING_BLOCK_ENTITY_TICKER); ++ ++ // Set the last tick time to max value, so other hoppers transferring into this hopper will set it to 7gt ++ // cooldown. Then when waking up, we make sure to not tick this hopper in the same gametick. ++ // This makes the observable hopper cooldown not be different from vanilla. ++ this.tickedGameTime = Long.MAX_VALUE; ++ return true; ++ } ++ return false; ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList<>(); +@@ -160,6 +205,14 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + } + } + ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ if (!(blockEntity).isOnCooldown() && ++ !(blockEntity).isSleeping() && ++ !state.getValue(HopperBlock.ENABLED)) { ++ (blockEntity).lithium$startSleeping(); ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping ++ + return false; + } + } +@@ -586,6 +639,17 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen + } + + public void setCooldown(int cooldownTime) { ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ if (cooldownTime == 7) { ++ if (this.tickedGameTime == Long.MAX_VALUE) { ++ this.sleepOnlyCurrentTick(); ++ } else { ++ this.wakeUpNow(); ++ } ++ } else if (cooldownTime > 0 && this.sleepingTicker != null) { ++ this.wakeUpNow(); ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + this.cooldownTime = cooldownTime; + } + +diff --git a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java +index a2ae4b47d742e7fb9809cfc4575517c06400ec61..e92863af578bb472a692482be3dddc1cc658f930 100644 +--- a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java +@@ -32,7 +32,7 @@ import net.minecraft.world.level.material.PushReaction; + import net.minecraft.world.phys.AABB; + import net.minecraft.world.phys.Vec3; + +-public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity implements WorldlyContainer { ++public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity implements WorldlyContainer, net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity { // DivineMC - lithium: block_entity_ticking.sleeping + public static final int COLUMNS = 9; + public static final int ROWS = 3; + public static final int CONTAINER_SIZE = 27; +@@ -48,6 +48,30 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl + private float progressOld; + @Nullable + private final DyeColor color; ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ 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; ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList<>(); +@@ -129,6 +153,12 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl + doNeighborUpdates(level, pos, state); + } + } ++ ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ if (this.animationStatus == ShulkerBoxBlockEntity.AnimationStatus.CLOSED && this.progressOld == 0.0f && this.progress == 0.0f) { ++ this.lithium$startSleeping(); ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + } + + public ShulkerBoxBlockEntity.AnimationStatus getAnimationStatus() { +@@ -169,6 +199,7 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl + + @Override + public boolean triggerEvent(int id, int type) { ++ if (this.sleepingTicker != null) this.wakeUpNow(); // DivineMC - lithium: block_entity_ticking.sleeping + if (id == 1) { + this.openCount = type; + if (type == 0) { +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index 1cdff5ee25e6ef66e6aae221e61eee7829b213ed..7a76396e7dedd886ce56dd6a578f6e64d8553abe 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -883,12 +883,22 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + (pos, ticker1) -> { + TickingBlockEntity tickingBlockEntity = this.createTicker(blockEntity, ticker); + if (ticker1 != null) { ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ if (blockEntity instanceof net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity sleepingBlockEntity) { ++ sleepingBlockEntity.lithium$setTickWrapper(ticker1); ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + ticker1.rebind(tickingBlockEntity); + return (LevelChunk.RebindableTickingBlockEntityWrapper)ticker1; + } else if (this.isInLevel()) { + LevelChunk.RebindableTickingBlockEntityWrapper rebindableTickingBlockEntityWrapper = new LevelChunk.RebindableTickingBlockEntityWrapper( + tickingBlockEntity + ); ++ // DivineMC start - lithium: block_entity_ticking.sleeping ++ if (blockEntity instanceof net.caffeinemc.mods.lithium.common.block.entity.SleepingBlockEntity sleepingBlockEntity) { ++ sleepingBlockEntity.lithium$setTickWrapper(rebindableTickingBlockEntityWrapper); ++ } ++ // DivineMC end - lithium: block_entity_ticking.sleeping + this.level.addBlockEntityTicker(rebindableTickingBlockEntityWrapper); + return rebindableTickingBlockEntityWrapper; + } else { diff --git a/divinemc-server/src/main/java/net/caffeinemc/mods/lithium/common/block/entity/SleepUntilTimeBlockEntityTickInvoker.java b/divinemc-server/src/main/java/net/caffeinemc/mods/lithium/common/block/entity/SleepUntilTimeBlockEntityTickInvoker.java new file mode 100644 index 0000000..7582b8a --- /dev/null +++ b/divinemc-server/src/main/java/net/caffeinemc/mods/lithium/common/block/entity/SleepUntilTimeBlockEntityTickInvoker.java @@ -0,0 +1,33 @@ +package net.caffeinemc.mods.lithium.common.block.entity; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.TickingBlockEntity; +import org.jetbrains.annotations.NotNull; + +public record SleepUntilTimeBlockEntityTickInvoker(BlockEntity sleepingBlockEntity, long sleepUntilTickExclusive, TickingBlockEntity delegate) implements TickingBlockEntity { + @Override + public void tick() { + long tickTime = this.sleepingBlockEntity.getLevel().getGameTime(); + if (tickTime >= this.sleepUntilTickExclusive) { + ((SleepingBlockEntity) this.sleepingBlockEntity).setTicker(this.delegate); + this.delegate.tick(); + } + } + + @Override + public boolean isRemoved() { + return this.sleepingBlockEntity.isRemoved(); + } + + @Override + public @NotNull BlockPos getPos() { + return this.sleepingBlockEntity.getBlockPos(); + } + + @Override + public @NotNull String getType() { + return BlockEntityType.getKey(this.sleepingBlockEntity.getType()).toString(); + } +} diff --git a/divinemc-server/src/main/java/net/caffeinemc/mods/lithium/common/block/entity/SleepingBlockEntity.java b/divinemc-server/src/main/java/net/caffeinemc/mods/lithium/common/block/entity/SleepingBlockEntity.java new file mode 100644 index 0000000..a0f54f8 --- /dev/null +++ b/divinemc-server/src/main/java/net/caffeinemc/mods/lithium/common/block/entity/SleepingBlockEntity.java @@ -0,0 +1,82 @@ +package net.caffeinemc.mods.lithium.common.block.entity; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.TickingBlockEntity; +import net.minecraft.world.level.chunk.LevelChunk; +import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; + +public interface SleepingBlockEntity { + TickingBlockEntity SLEEPING_BLOCK_ENTITY_TICKER = new TickingBlockEntity() { + public void tick() { + } + + public boolean isRemoved() { + return false; + } + + public @Nullable BlockPos getPos() { + return null; + } + + public @NotNull String getType() { + return ""; + } + }; + + LevelChunk.RebindableTickingBlockEntityWrapper lithium$getTickWrapper(); + + void lithium$setTickWrapper(LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper); + + TickingBlockEntity lithium$getSleepingTicker(); + + void lithium$setSleepingTicker(TickingBlockEntity sleepingTicker); + + default boolean lithium$startSleeping() { + if (this.isSleeping()) { + return false; + } + + LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = this.lithium$getTickWrapper(); + if (tickWrapper == null) { + return false; + } + this.lithium$setSleepingTicker(tickWrapper.ticker); + tickWrapper.rebind(SleepingBlockEntity.SLEEPING_BLOCK_ENTITY_TICKER); + return true; + } + + default void sleepOnlyCurrentTick() { + TickingBlockEntity sleepingTicker = this.lithium$getSleepingTicker(); + LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = this.lithium$getTickWrapper(); + if (sleepingTicker == null) { + sleepingTicker = tickWrapper.ticker; + } + Level world = ((BlockEntity) this).getLevel(); + tickWrapper.rebind(new SleepUntilTimeBlockEntityTickInvoker((BlockEntity) this, world.getGameTime() + 1, sleepingTicker)); + this.lithium$setSleepingTicker(null); + } + + default void wakeUpNow() { + TickingBlockEntity sleepingTicker = this.lithium$getSleepingTicker(); + if (sleepingTicker == null) { + return; + } + this.setTicker(sleepingTicker); + this.lithium$setSleepingTicker(null); + } + + default void setTicker(TickingBlockEntity delegate) { + LevelChunk.RebindableTickingBlockEntityWrapper tickWrapper = this.lithium$getTickWrapper(); + if (tickWrapper == null) { + return; + } + tickWrapper.rebind(delegate); + } + + default boolean isSleeping() { + return this.lithium$getSleepingTicker() != null; + } +}