From 67098704567e814ec8cc5c78ca5944113c225a6a Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Mon, 21 Aug 2023 13:17:47 -0700 Subject: [PATCH] Improve and fix VirtualRenderWorld - Fix calling VirtualRenderWorld#getExistingBlockEntity causing a crash - Bump version --- gradle.properties | 2 +- .../flywheel/core/virtual/VirtualChunk.java | 252 ++++++------- .../core/virtual/VirtualChunkSection.java | 14 +- .../core/virtual/VirtualChunkSource.java | 53 +-- .../core/virtual/VirtualEmptyBlockGetter.java | 13 +- .../virtual/VirtualLevelEntityGetter.java | 12 +- .../core/virtual/VirtualRenderWorld.java | 344 +++++++++++------- 7 files changed, 381 insertions(+), 309 deletions(-) diff --git a/gradle.properties b/gradle.properties index 9bd695b3f..ccf465b19 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs = -Xmx3G org.gradle.daemon = false # mod version info -mod_version = 0.6.9 +mod_version = 0.6.10 artifact_minecraft_version = 1.18.2 minecraft_version = 1.18.2 diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java index eaed3e59f..1160163c5 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunk.java @@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.longs.LongSets; import it.unimi.dsi.fastutil.shorts.ShortList; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; @@ -36,13 +37,11 @@ import net.minecraft.world.ticks.BlackholeTickAccess; import net.minecraft.world.ticks.TickContainerAccess; public class VirtualChunk extends ChunkAccess { + public final VirtualRenderWorld world; - final VirtualRenderWorld world; - boolean needsLight; - final int x; - final int z; + private final VirtualChunkSection[] sections; - private final LevelChunkSection[] sections; + private boolean needsLight; public VirtualChunk(VirtualRenderWorld world, int x, int z) { super(new ChunkPos(x, z), UpgradeData.EMPTY, world, world.registryAccess() @@ -50,34 +49,39 @@ public class VirtualChunk extends ChunkAccess { .orElseThrow(), 0L, null, null); this.world = world; - this.needsLight = true; - this.x = x; - this.z = z; int sectionCount = world.getSectionsCount(); - this.sections = new LevelChunkSection[sectionCount]; + this.sections = new VirtualChunkSection[sectionCount]; for (int i = 0; i < sectionCount; i++) { sections[i] = new VirtualChunkSection(this, i << 4); } + this.needsLight = true; + Mods.STARLIGHT.executeIfInstalled(() -> () -> { - ((ExtendedChunk)this).setBlockNibbles(StarLightEngine.getFilledEmptyLight(this)); - ((ExtendedChunk)this).setSkyNibbles(StarLightEngine.getFilledEmptyLight(this)); + ((ExtendedChunk) this).setBlockNibbles(StarLightEngine.getFilledEmptyLight(this)); + ((ExtendedChunk) this).setSkyNibbles(StarLightEngine.getFilledEmptyLight(this)); }); } @Override - public Stream getLights() { - return world.blocksAdded.entrySet() - .stream() - .filter(it -> { - BlockPos blockPos = it.getKey(); - boolean chunkContains = blockPos.getX() >> 4 == x && blockPos.getZ() >> 4 == z; - return chunkContains && it.getValue() - .getLightEmission(world, blockPos) != 0; - }) - .map(Map.Entry::getKey); + @Nullable + public BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving) { + return null; + } + + @Override + public void setBlockEntity(BlockEntity blockEntity) { + } + + @Override + public void addEntity(Entity entity) { + } + + @Override + public Set getBlockEntitiesPos() { + return Collections.emptySet(); } @Override @@ -85,121 +89,33 @@ public class VirtualChunk extends ChunkAccess { return sections; } - @Override - public ChunkStatus getStatus() { - return ChunkStatus.LIGHT; - } - - @Nullable - @Override - public BlockState setBlockState(BlockPos p_177436_1_, BlockState p_177436_2_, boolean p_177436_3_) { - return null; - } - - @Override - public void setBlockEntity(BlockEntity p_177426_2_) {} - - @Override - public void addEntity(Entity p_76612_1_) {} - - @Override - public Set getBlockEntitiesPos() { - return null; - } - @Override public Collection> getHeightmaps() { + return Collections.emptySet(); + } + + @Override + public void setHeightmap(Heightmap.Types type, long[] data) { + } + + @Override + public Heightmap getOrCreateHeightmapUnprimed(Heightmap.Types type) { return null; } @Override - public void setHeightmap(Heightmap.Types p_201607_1_, long[] p_201607_2_) {} - - @Override - public Heightmap getOrCreateHeightmapUnprimed(Heightmap.Types p_217303_1_) { - return null; - } - - @Override - public int getHeight(Heightmap.Types p_201576_1_, int p_201576_2_, int p_201576_3_) { + public int getHeight(Heightmap.Types type, int x, int z) { return 0; } @Override - public void setUnsaved(boolean p_177427_1_) {} - - @Override - public boolean isUnsaved() { - return false; - } - - @Override - public void removeBlockEntity(BlockPos p_177425_1_) {} - - @Override - public ShortList[] getPostProcessing() { - return new ShortList[0]; - } - @Nullable - @Override - public CompoundTag getBlockEntityNbt(BlockPos p_201579_1_) { - return null; - } - - @Nullable - @Override - public CompoundTag getBlockEntityNbtForSaving(BlockPos p_223134_1_) { + public StructureStart getStartForFeature(ConfiguredStructureFeature structure) { return null; } @Override - public UpgradeData getUpgradeData() { - return null; - } - - @Override - public void setInhabitedTime(long p_177415_1_) {} - - @Override - public long getInhabitedTime() { - return 0; - } - - @Override - public boolean isLightCorrect() { - return needsLight; - } - - @Override - public void setLightCorrect(boolean needsLight) { - this.needsLight = needsLight; - } - - @Nullable - @Override - public BlockEntity getBlockEntity(BlockPos pos) { - return null; - } - - @Override - public BlockState getBlockState(BlockPos pos) { - return world.getBlockState(pos); - } - - @Override - public FluidState getFluidState(BlockPos p_204610_1_) { - return null; - } - - @Override - @Nullable - public StructureStart getStartForFeature(ConfiguredStructureFeature pStructure) { - return null; - } - - @Override - public void setStartForFeature(ConfiguredStructureFeature pStructure, StructureStart pStart) { + public void setStartForFeature(ConfiguredStructureFeature structure, StructureStart start) { } @Override @@ -208,16 +124,16 @@ public class VirtualChunk extends ChunkAccess { } @Override - public void setAllStarts(Map, StructureStart> pStructureStarts) { + public void setAllStarts(Map, StructureStart> structureStarts) { } @Override - public LongSet getReferencesForFeature(ConfiguredStructureFeature pStructure) { + public LongSet getReferencesForFeature(ConfiguredStructureFeature structure) { return LongSets.emptySet(); } @Override - public void addReferenceForFeature(ConfiguredStructureFeature pStructure, long pReference) { + public void addReferenceForFeature(ConfiguredStructureFeature structure, long reference) { } @Override @@ -226,17 +142,60 @@ public class VirtualChunk extends ChunkAccess { } @Override - public void setAllReferences(Map, LongSet> pStructureReferences) { + public void setAllReferences(Map, LongSet> structureReferences) { } @Override - public int getHeight() { - return world.getHeight(); + public void setUnsaved(boolean unsaved) { } @Override - public int getMinBuildHeight() { - return world.getMinBuildHeight(); + public boolean isUnsaved() { + return false; + } + + @Override + public ChunkStatus getStatus() { + return ChunkStatus.LIGHT; + } + + @Override + public void removeBlockEntity(BlockPos pos) { + } + + @Override + public ShortList[] getPostProcessing() { + return new ShortList[0]; + } + + @Override + @Nullable + public CompoundTag getBlockEntityNbt(BlockPos pos) { + return null; + } + + @Override + @Nullable + public CompoundTag getBlockEntityNbtForSaving(BlockPos pos) { + return null; + } + + @Override + public Stream getLights() { + return world.blockStates.entrySet() + .stream() + .filter(it -> { + BlockPos blockPos = it.getKey(); + boolean chunkContains = SectionPos.blockToSectionCoord(blockPos.getX()) == chunkPos.x && SectionPos.blockToSectionCoord(blockPos.getZ()) == chunkPos.z; + return chunkContains && it.getValue() + .getLightEmission(world, blockPos) != 0; + }) + .map(Map.Entry::getKey); + } + + @Override + public TickContainerAccess getBlockTicks() { + return BlackholeTickAccess.emptyContainer(); } @Override @@ -246,12 +205,41 @@ public class VirtualChunk extends ChunkAccess { @Override public TicksToSave getTicksForSerialization() { - return null; + throw new UnsupportedOperationException(); } @Override - public TickContainerAccess getBlockTicks() { - return BlackholeTickAccess.emptyContainer(); + public long getInhabitedTime() { + return 0; } + @Override + public void setInhabitedTime(long amount) { + } + + @Override + public boolean isLightCorrect() { + return needsLight; + } + + @Override + public void setLightCorrect(boolean lightCorrect) { + this.needsLight = lightCorrect; + } + + @Override + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + return world.getBlockEntity(pos); + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return world.getBlockState(pos); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return world.getFluidState(pos); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSection.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSection.java index a3d4f1952..01414b065 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSection.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSection.java @@ -3,10 +3,10 @@ package com.jozufozu.flywheel.core.virtual; import net.minecraft.core.Registry; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.material.FluidState; public class VirtualChunkSection extends LevelChunkSection { - - public VirtualChunk owner; + public final VirtualChunk owner; public final int xStart; public final int yStart; @@ -32,8 +32,12 @@ public class VirtualChunkSection extends LevelChunkSection { } @Override - public BlockState setBlockState(int p_177484_1_, int p_177484_2_, int p_177484_3_, BlockState p_177484_4_, - boolean p_177484_5_) { - throw new IllegalStateException("Chunk sections should not be mutated in a fake world."); + public FluidState getFluidState(int x, int y, int z) { + return getBlockState(x, y, z).getFluidState(); + } + + @Override + public BlockState setBlockState(int x, int y, int z, BlockState state, boolean useLocks) { + throw new UnsupportedOperationException("Chunk sections cannot be mutated in a fake world."); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSource.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSource.java index 7fa73d38d..ff91f12da 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSource.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualChunkSource.java @@ -1,62 +1,65 @@ package com.jozufozu.flywheel.core.virtual; -import java.util.HashMap; import java.util.function.BooleanSupplier; -import net.minecraft.world.level.BlockGetter; +import org.jetbrains.annotations.Nullable; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.lighting.LevelLightEngine; public class VirtualChunkSource extends ChunkSource { private final VirtualRenderWorld world; - - public final HashMap chunks = new HashMap<>(); + private final Long2ObjectMap chunks = new Long2ObjectOpenHashMap<>(); public VirtualChunkSource(VirtualRenderWorld world) { this.world = world; } - @Override - public BlockGetter getChunkForLighting(int x, int z) { - return getChunk(x, z); - } - @Override public Level getLevel() { return world; } - @Override - public ChunkAccess getChunk(int x, int z, ChunkStatus status, boolean p_212849_4_) { - return getChunk(x, z); - } - public ChunkAccess getChunk(int x, int z) { long pos = ChunkPos.asLong(x, z); - return chunks.computeIfAbsent(pos, $ -> new VirtualChunk(world, x, z)); } + @Override + @Nullable + public LevelChunk getChunk(int x, int z, boolean load) { + return null; + } + + @Override + @Nullable + public ChunkAccess getChunk(int x, int z, ChunkStatus status, boolean load) { + return getChunk(x, z); + } + + @Override + public void tick(BooleanSupplier hasTimeLeft, boolean tickChunks) { + } + @Override public String gatherStats() { - return "WrappedChunkProvider"; - } - - @Override - public LevelLightEngine getLightEngine() { - return world.getLightEngine(); - } - - @Override - public void tick(BooleanSupplier p_202162_, boolean p_202163_) { + return "VirtualChunkSource"; } @Override public int getLoadedChunksCount() { return 0; } + + @Override + public LevelLightEngine getLightEngine() { + return world.getLightEngine(); + } } diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualEmptyBlockGetter.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualEmptyBlockGetter.java index ebfbe9389..8785d3ada 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualEmptyBlockGetter.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualEmptyBlockGetter.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.core.virtual; +import org.jetbrains.annotations.Nullable; + import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -32,6 +34,7 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter { } @Override + @Nullable default BlockEntity getBlockEntity(BlockPos pos) { return null; } @@ -99,7 +102,7 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter { } @Override - public void onBlockEmissionIncrease(BlockPos pos, int p_164456_) { + public void onBlockEmissionIncrease(BlockPos pos, int emissionLevel) { } @Override @@ -108,16 +111,16 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter { } @Override - public int runUpdates(int p_164449_, boolean p_164450_, boolean p_164451_) { - return p_164449_; + public int runUpdates(int pos, boolean isQueueEmpty, boolean updateBlockLight) { + return pos; } @Override - public void updateSectionStatus(SectionPos pos, boolean p_75838_) { + public void updateSectionStatus(SectionPos pos, boolean isQueueEmpty) { } @Override - public void enableLightSources(ChunkPos pos, boolean p_164453_) { + public void enableLightSources(ChunkPos pos, boolean isQueueEmpty) { } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualLevelEntityGetter.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualLevelEntityGetter.java index 2edd3fdf3..67ce31b48 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualLevelEntityGetter.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualLevelEntityGetter.java @@ -10,14 +10,13 @@ import net.minecraft.world.level.entity.LevelEntityGetter; import net.minecraft.world.phys.AABB; public class VirtualLevelEntityGetter implements LevelEntityGetter { - @Override - public T get(int p_156931_) { + public T get(int id) { return null; } @Override - public T get(UUID pUuid) { + public T get(UUID uuid) { return null; } @@ -27,15 +26,14 @@ public class VirtualLevelEntityGetter implements LevelEn } @Override - public void get(EntityTypeTest p_156935_, Consumer p_156936_) { + public void get(EntityTypeTest test, Consumer consumer) { } @Override - public void get(AABB p_156937_, Consumer p_156938_) { + public void get(AABB boundingBox, Consumer consumer) { } @Override - public void get(EntityTypeTest p_156932_, AABB p_156933_, Consumer p_156934_) { + public void get(EntityTypeTest test, AABB bounds, Consumer consumer) { } - } diff --git a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java index 39574d808..8e3266ed7 100644 --- a/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java +++ b/src/main/java/com/jozufozu/flywheel/core/virtual/VirtualRenderWorld.java @@ -3,16 +3,15 @@ package com.jozufozu.flywheel.core.virtual; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; import javax.annotation.Nullable; import com.jozufozu.flywheel.api.FlywheelWorld; +import it.unimi.dsi.fastutil.objects.Object2ShortMap; +import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; @@ -33,48 +32,54 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.entity.LevelEntityGetter; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.saveddata.maps.MapItemSavedData; import net.minecraft.world.level.storage.WritableLevelData; import net.minecraft.world.scores.Scoreboard; import net.minecraft.world.ticks.LevelTickAccess; public class VirtualRenderWorld extends Level implements FlywheelWorld { - public final Map blocksAdded = new HashMap<>(); - public final Map besAdded = new HashMap<>(); - public final Set spannedSections = new HashSet<>(); - private final BlockPos.MutableBlockPos scratch = new BlockPos.MutableBlockPos(); - protected final Level level; - protected final LevelLightEngine lighter; - protected final VirtualChunkSource chunkSource; - protected final LevelEntityGetter entityGetter = new VirtualLevelEntityGetter<>(); - - protected final int height; protected final int minBuildHeight; + protected final int height; protected final Vec3i biomeOffset; + protected final VirtualChunkSource chunkSource; + protected final LevelLightEngine lightEngine; + + protected final Map blockStates = new HashMap<>(); + protected final Map blockEntities = new HashMap<>(); + protected final Object2ShortMap nonEmptyBlockCounts = new Object2ShortOpenHashMap<>(); + + protected final LevelEntityGetter entityGetter = new VirtualLevelEntityGetter<>(); + + protected final BlockPos.MutableBlockPos scratchPos = new BlockPos.MutableBlockPos(); + public VirtualRenderWorld(Level level) { - this(level, Vec3i.ZERO, level.getHeight(), level.getMinBuildHeight()); + this(level, Vec3i.ZERO); } public VirtualRenderWorld(Level level, Vec3i biomeOffset) { - this(level, biomeOffset, level.getHeight(), level.getMinBuildHeight()); + this(level, level.getMinBuildHeight(), level.getHeight(), biomeOffset); } - public VirtualRenderWorld(Level level, Vec3i biomeOffset, int height, int minBuildHeight) { - super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionTypeRegistration(), level::getProfiler, + public VirtualRenderWorld(Level level, int minBuildHeight, int height, Vec3i biomeOffset) { + super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionTypeRegistration(), level.getProfilerSupplier(), true, false, 0); - this.biomeOffset = biomeOffset; this.level = level; - this.height = nextMultipleOf16(height); this.minBuildHeight = nextMultipleOf16(minBuildHeight); + this.height = nextMultipleOf16(height); + this.biomeOffset = biomeOffset; + this.chunkSource = new VirtualChunkSource(this); - this.lighter = new LevelLightEngine(chunkSource, true, false); + this.lightEngine = new LevelLightEngine(chunkSource, true, false); } /** @@ -89,67 +94,145 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } } - /** - * Run this after you're done using setBlock(). - */ - public void runLightingEngine() { - for (Map.Entry entry : blocksAdded.entrySet()) { - BlockPos pos = entry.getKey(); - BlockState state = entry.getValue(); - int light = state.getLightEmission(this, pos); - if (light > 0) { - lighter.onBlockEmissionIncrease(pos, light); - } - } + public void clear() { + blockStates.clear(); + blockEntities.clear(); - lighter.runUpdates(Integer.MAX_VALUE, false, false); + nonEmptyBlockCounts.forEach((sectionPos, nonEmptyBlockCount) -> { + if (nonEmptyBlockCount > 0) { + lightEngine.updateSectionStatus(sectionPos, true); + } + }); + + nonEmptyBlockCounts.clear(); + + runLightEngine(); } public void setBlockEntities(Collection blockEntities) { - besAdded.clear(); - blockEntities.forEach(be -> besAdded.put(be.getBlockPos(), be)); + this.blockEntities.clear(); + blockEntities.forEach(this::setBlockEntity); } - public void clear() { - blocksAdded.clear(); + /** + * Run this after you're done using setBlock(). + */ + public void runLightEngine() { + blockStates.forEach((pos, state) -> { + int light = state.getLightEmission(this, pos); + if (light > 0) { + lightEngine.onBlockEmissionIncrease(pos, light); + } + }); + + lightEngine.runUpdates(Integer.MAX_VALUE, false, false); } // MEANINGFUL OVERRIDES @Override - public boolean setBlock(BlockPos pos, BlockState newState, int flags) { - blocksAdded.put(pos, newState); + public LevelChunk getChunk(int x, int z) { + throw new UnsupportedOperationException(); + } + + public ChunkAccess actuallyGetChunk(int x, int z) { + return getChunk(x, z, ChunkStatus.FULL); + } + + @Override + public ChunkAccess getChunk(BlockPos pos) { + return actuallyGetChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); + } + + @Override + public boolean setBlock(BlockPos pos, BlockState newState, int flags, int recursionLeft) { + if (isOutsideBuildHeight(pos)) { + return false; + } + + pos = pos.immutable(); + + BlockState oldState = getBlockState(pos); + if (oldState == newState) { + return false; + } + + blockStates.put(pos, newState); SectionPos sectionPos = SectionPos.of(pos); - if (spannedSections.add(sectionPos)) { - lighter.updateSectionStatus(sectionPos, false); + short nonEmptyBlockCount = nonEmptyBlockCounts.getShort(sectionPos); + boolean prevEmpty = nonEmptyBlockCount == 0; + if (!oldState.isAir()) { + --nonEmptyBlockCount; + } + if (!newState.isAir()) { + ++nonEmptyBlockCount; + } + nonEmptyBlockCounts.put(sectionPos, nonEmptyBlockCount); + boolean nowEmpty = nonEmptyBlockCount == 0; + + if (prevEmpty != nowEmpty) { + lightEngine.updateSectionStatus(sectionPos, nowEmpty); } if ((flags & Block.UPDATE_SUPPRESS_LIGHT) == 0) { - lighter.checkBlock(pos); + lightEngine.checkBlock(pos); } return true; } - @Override - public int getHeight() { - return height; - } - - @Override - public int getMinBuildHeight() { - return minBuildHeight; - } - - @Override - public ChunkSource getChunkSource() { - return chunkSource; - } - @Override public LevelLightEngine getLightEngine() { - return lighter; + return lightEngine; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + if (isOutsideBuildHeight(pos)) { + return Blocks.VOID_AIR.defaultBlockState(); + } + BlockState state = blockStates.get(pos); + if (state != null) { + return state; + } + return Blocks.AIR.defaultBlockState(); + } + + public BlockState getBlockState(int x, int y, int z) { + return getBlockState(scratchPos.set(x, y, z)); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + if (isOutsideBuildHeight(pos)) { + return Fluids.EMPTY.defaultFluidState(); + } + return getBlockState(pos).getFluidState(); + } + + @Override + @Nullable + public BlockEntity getBlockEntity(BlockPos pos) { + if (!isOutsideBuildHeight(pos)) { + return blockEntities.get(pos); + } + return null; + } + + @Override + public void setBlockEntity(BlockEntity blockEntity) { + BlockPos pos = blockEntity.getBlockPos(); + if (!isOutsideBuildHeight(pos)) { + blockEntities.put(pos, blockEntity); + } + } + + @Override + public void removeBlockEntity(BlockPos pos) { + if (!isOutsideBuildHeight(pos)) { + blockEntities.remove(pos); + } } @Override @@ -158,52 +241,39 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } @Override - public BlockState getBlockState(@Nullable BlockPos pos) { - BlockState state = blocksAdded.get(pos); - if (state != null) - return state; - return Blocks.AIR.defaultBlockState(); + public ChunkSource getChunkSource() { + return chunkSource; } @Override - public boolean setBlockAndUpdate(BlockPos pos, BlockState state) { - return setBlock(pos, state, 0); + public int getMinBuildHeight() { + return minBuildHeight; } @Override - @Nullable - public BlockEntity getBlockEntity(BlockPos pos) { - return besAdded.get(pos); - } - - @Override - public boolean isStateAtPosition(BlockPos pos, Predicate condition) { - return condition.test(getBlockState(pos)); - } - - public BlockState getBlockState(int x, int y, int z) { - return getBlockState(scratch.set(x, y, z)); + public int getHeight() { + return height; } // BIOME OFFSET @Override - public Holder getBiome(BlockPos pPos) { - return super.getBiome(pPos.offset(biomeOffset)); + public Holder getBiome(BlockPos pos) { + return super.getBiome(pos.offset(biomeOffset)); } @Override - public Holder getUncachedNoiseBiome(int pX, int pY, int pZ) { + public Holder getNoiseBiome(int x, int y, int z) { // Control flow should never reach this method, // so we add biomeOffset in case some other mod calls this directly. - return level.getUncachedNoiseBiome(pX + biomeOffset.getX(), pY + biomeOffset.getY(), pZ + biomeOffset.getZ()); + return level.getNoiseBiome(x + biomeOffset.getX(), y + biomeOffset.getY(), z + biomeOffset.getZ()); } @Override - public Holder getNoiseBiome(int pX, int pY, int pZ) { + public Holder getUncachedNoiseBiome(int x, int y, int z) { // Control flow should never reach this method, // so we add biomeOffset in case some other mod calls this directly. - return level.getNoiseBiome(pX + biomeOffset.getX(), pY + biomeOffset.getY(), pZ + biomeOffset.getZ()); + return level.getUncachedNoiseBiome(x + biomeOffset.getX(), y + biomeOffset.getY(), z + biomeOffset.getZ()); } // RENDERING CONSTANTS @@ -214,22 +284,27 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } @Override - public float getShade(Direction p_230487_1_, boolean p_230487_2_) { + public float getShade(Direction direction, boolean shade) { return 1f; } - // THIN WRAPPERS AHEAD + // THIN WRAPPERS + + @Override + public Scoreboard getScoreboard() { + return level.getScoreboard(); + } + + @Override + public RecipeManager getRecipeManager() { + return level.getRecipeManager(); + } @Override public BiomeManager getBiomeManager() { return level.getBiomeManager(); } - @Override - public RegistryAccess registryAccess() { - return level.registryAccess(); - } - @Override public LevelTickAccess getBlockTicks() { return level.getBlockTicks(); @@ -241,21 +316,44 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } @Override - public RecipeManager getRecipeManager() { - return level.getRecipeManager(); + public RegistryAccess registryAccess() { + return level.registryAccess(); + } + + // ADDITIONAL OVERRRIDES + + @Override + public void updateNeighbourForOutputSignal(BlockPos pos, Block block) { } @Override - public int getFreeMapId() { - return level.getFreeMapId(); + public boolean isLoaded(BlockPos pos) { + return true; } @Override - public Scoreboard getScoreboard() { - return level.getScoreboard(); + public boolean isAreaLoaded(BlockPos center, int range) { + return true; } - // UNIMPORTANT CONSTANTS + // UNIMPORTANT IMPLEMENTATIONS + + @Override + public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) { + } + + @Override + public void playSound(@Nullable Player player, double x, double y, double z, SoundEvent sound, SoundSource category, float volume, float pitch) { + } + + @Override + public void playSound(@Nullable Player player, Entity entity, SoundEvent sound, SoundSource category, float volume, float pitch) { + } + + @Override + public String gatherChunkSourceStats() { + return ""; + } @Override @Nullable @@ -270,13 +368,24 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { } @Override - public boolean isLoaded(BlockPos pos) { - return true; + public void setMapData(String mapId, MapItemSavedData data) { } @Override - public boolean isAreaLoaded(BlockPos center, int range) { - return true; + public int getFreeMapId() { + return 0; + } + + @Override + public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) { + } + + @Override + public void levelEvent(@Nullable Player player, int type, BlockPos pos, int data) { + } + + @Override + public void gameEvent(@Nullable Entity entity, GameEvent event, BlockPos pos) { } @Override @@ -284,39 +393,6 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld { return Collections.emptyList(); } - @Override - public String gatherChunkSourceStats() { - return ""; - } - - // NOOP - - @Override - public void levelEvent(@Nullable Player player, int type, BlockPos pos, int data) {} - - @Override - public void playSound(@Nullable Player player, double x, double y, double z, SoundEvent soundIn, - SoundSource category, float volume, float pitch) {} - - @Override - public void playSound(@Nullable Player p_217384_1_, Entity p_217384_2_, SoundEvent p_217384_3_, - SoundSource p_217384_4_, float p_217384_5_, float p_217384_6_) {} - - @Override - public void setMapData(String pMapId, MapItemSavedData pData) {} - - @Override - public void destroyBlockProgress(int breakerId, BlockPos pos, int progress) {} - - @Override - public void updateNeighbourForOutputSignal(BlockPos p_175666_1_, Block p_175666_2_) {} - - @Override - public void gameEvent(@Nullable Entity pEntity, GameEvent pEvent, BlockPos pPos) {} - - @Override - public void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) {} - // Override Starlight's ExtendedWorld interface methods: public LevelChunk getChunkAtImmediately(final int chunkX, final int chunkZ) {