diff --git a/docs/migration/DIVERGENCES.md b/docs/migration/DIVERGENCES.md index c9fe111..cef070c 100644 --- a/docs/migration/DIVERGENCES.md +++ b/docs/migration/DIVERGENCES.md @@ -16,6 +16,7 @@ audit, before extending this list. | --- | --- | | Raw microblock recipe material lookup | Skips `MissingMicroMaterial`, whose recovery item is stone, so sawing stone selects the real stone material. Existing missing-material microblocks retain their recovery behavior. This fixes a pre-existing Scala bug. | | `BlockMicroMaterial.createAndRegister(Block, Seq, String)` | Registers every supplied metadata value instead of only zero, including dark-oak logs and leaves and their legacy-name remaps. This fixes a pre-existing Scala bug; material IDs are rebuilt through the existing registry mapping. | +| Chunk scheduled-tick restoration | Reactivates chunks with restored scheduled ticks so their callbacks resume at the saved deadlines. The Scala implementation restored entries without activating their chunk. NBT layout and duplicate-scheduling behavior are unchanged. | | `IDWriter` before `setMax` | `read`/`write` throw `IllegalStateException` instead of `NullPointerException`. Uninitialized use is unsupported. | | `PacketScheduler` invalid `maskWidth` | Throws `IllegalArgumentException` instead of `scala.MatchError`. Valid widths remain 1, 2, 4 and 8. | | `NormalOcclusionTest.apply(Traversable, Traversable)` | Materializes both inputs into Java lists before testing instead of nested Scala `forall`. Finite, side-effect-free collections give the same result; eager traversal can change side effects, failure timing and termination for unusual inputs. | diff --git a/src/main/scala/codechicken/multipart/TickScheduler.java b/src/main/scala/codechicken/multipart/TickScheduler.java index 83bc203..3ec0d36 100644 --- a/src/main/scala/codechicken/multipart/TickScheduler.java +++ b/src/main/scala/codechicken/multipart/TickScheduler.java @@ -302,6 +302,9 @@ public final class TickScheduler { false)); } } + if (!tickList.isEmpty()) { + scheduler().tickChunks.add(this); + } } @Override diff --git a/src/test/java/codechicken/multipart/TickSchedulerPersistenceRegressionTest.java b/src/test/java/codechicken/multipart/TickSchedulerPersistenceRegressionTest.java new file mode 100644 index 0000000..b172867 --- /dev/null +++ b/src/test/java/codechicken/multipart/TickSchedulerPersistenceRegressionTest.java @@ -0,0 +1,55 @@ +package codechicken.multipart; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.ChunkPosition; +import net.minecraft.world.chunk.Chunk; + +import org.junit.jupiter.api.Test; + +class TickSchedulerPersistenceRegressionTest { + + @Test + void savedTicksResumeAfterChunkReloadAndFireOnlyOnce() { + TickScheduler.WorldTickScheduler world = new TickScheduler.WorldTickScheduler(null); + Chunk chunk = new Chunk(null, 2, -3); + TileMultipart tile = new TileMultipart(); + tile.xCoord = 35; + tile.yCoord = 64; + tile.zCoord = -44; + int[] calls = { 0 }; + TMultiPart part = new TMultiPart() { + + @Override + public String getType() { + return "test:restored-tick"; + } + + @Override + public void scheduledTick() { + calls[0]++; + } + }; + tile.setPartList(Collections.singletonList(part)); + part.bind(tile); + chunk.chunkTileEntityMap.put(new ChunkPosition(3, 64, 4), tile); + TickScheduler.ChunkTickScheduler scheduler = new TickScheduler.ChunkTickScheduler(chunk, world); + scheduler.scheduleTick(part, 2, false); + NBTTagCompound saved = new NBTTagCompound(); + scheduler.saveData(saved); + assertEquals(1, saved.getTagList("multipartTicks", 10).tagCount()); + scheduler.unload(); + TickScheduler.ChunkTickScheduler restored = new TickScheduler.ChunkTickScheduler(chunk, world); + restored.loadData(saved); + restored.scheduleTick(part, 0, false); + for (int tick = 0; tick < 4; tick++) { + world.preTick(); + world.postTick(); + assertEquals(tick < 2 ? 0 : 1, calls[0]); + } + } + +}