Improve and fix VirtualRenderWorld

- Fix calling VirtualRenderWorld#getExistingBlockEntity causing a crash
- Bump version
This commit is contained in:
PepperCode1 2023-08-21 13:17:47 -07:00
parent 833edce4e5
commit 6709870456
7 changed files with 381 additions and 309 deletions

View file

@ -2,7 +2,7 @@ org.gradle.jvmargs = -Xmx3G
org.gradle.daemon = false org.gradle.daemon = false
# mod version info # mod version info
mod_version = 0.6.9 mod_version = 0.6.10
artifact_minecraft_version = 1.18.2 artifact_minecraft_version = 1.18.2
minecraft_version = 1.18.2 minecraft_version = 1.18.2

View file

@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.longs.LongSets;
import it.unimi.dsi.fastutil.shorts.ShortList; import it.unimi.dsi.fastutil.shorts.ShortList;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
@ -36,13 +37,11 @@ import net.minecraft.world.ticks.BlackholeTickAccess;
import net.minecraft.world.ticks.TickContainerAccess; import net.minecraft.world.ticks.TickContainerAccess;
public class VirtualChunk extends ChunkAccess { public class VirtualChunk extends ChunkAccess {
public final VirtualRenderWorld world;
final VirtualRenderWorld world; private final VirtualChunkSection[] sections;
boolean needsLight;
final int x;
final int z;
private final LevelChunkSection[] sections; private boolean needsLight;
public VirtualChunk(VirtualRenderWorld world, int x, int z) { public VirtualChunk(VirtualRenderWorld world, int x, int z) {
super(new ChunkPos(x, z), UpgradeData.EMPTY, world, world.registryAccess() super(new ChunkPos(x, z), UpgradeData.EMPTY, world, world.registryAccess()
@ -50,34 +49,39 @@ public class VirtualChunk extends ChunkAccess {
.orElseThrow(), 0L, null, null); .orElseThrow(), 0L, null, null);
this.world = world; this.world = world;
this.needsLight = true;
this.x = x;
this.z = z;
int sectionCount = world.getSectionsCount(); int sectionCount = world.getSectionsCount();
this.sections = new LevelChunkSection[sectionCount]; this.sections = new VirtualChunkSection[sectionCount];
for (int i = 0; i < sectionCount; i++) { for (int i = 0; i < sectionCount; i++) {
sections[i] = new VirtualChunkSection(this, i << 4); sections[i] = new VirtualChunkSection(this, i << 4);
} }
this.needsLight = true;
Mods.STARLIGHT.executeIfInstalled(() -> () -> { Mods.STARLIGHT.executeIfInstalled(() -> () -> {
((ExtendedChunk)this).setBlockNibbles(StarLightEngine.getFilledEmptyLight(this)); ((ExtendedChunk) this).setBlockNibbles(StarLightEngine.getFilledEmptyLight(this));
((ExtendedChunk)this).setSkyNibbles(StarLightEngine.getFilledEmptyLight(this)); ((ExtendedChunk) this).setSkyNibbles(StarLightEngine.getFilledEmptyLight(this));
}); });
} }
@Override @Override
public Stream<BlockPos> getLights() { @Nullable
return world.blocksAdded.entrySet() public BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving) {
.stream() return null;
.filter(it -> { }
BlockPos blockPos = it.getKey();
boolean chunkContains = blockPos.getX() >> 4 == x && blockPos.getZ() >> 4 == z; @Override
return chunkContains && it.getValue() public void setBlockEntity(BlockEntity blockEntity) {
.getLightEmission(world, blockPos) != 0; }
})
.map(Map.Entry::getKey); @Override
public void addEntity(Entity entity) {
}
@Override
public Set<BlockPos> getBlockEntitiesPos() {
return Collections.emptySet();
} }
@Override @Override
@ -85,121 +89,33 @@ public class VirtualChunk extends ChunkAccess {
return sections; 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<BlockPos> getBlockEntitiesPos() {
return null;
}
@Override @Override
public Collection<Map.Entry<Heightmap.Types, Heightmap>> getHeightmaps() { public Collection<Map.Entry<Heightmap.Types, Heightmap>> getHeightmaps() {
return Collections.emptySet();
}
@Override
public void setHeightmap(Heightmap.Types type, long[] data) {
}
@Override
public Heightmap getOrCreateHeightmapUnprimed(Heightmap.Types type) {
return null; return null;
} }
@Override @Override
public void setHeightmap(Heightmap.Types p_201607_1_, long[] p_201607_2_) {} public int getHeight(Heightmap.Types type, int x, int z) {
@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_) {
return 0; return 0;
} }
@Override @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 @Nullable
@Override public StructureStart getStartForFeature(ConfiguredStructureFeature<?, ?> structure) {
public CompoundTag getBlockEntityNbt(BlockPos p_201579_1_) {
return null;
}
@Nullable
@Override
public CompoundTag getBlockEntityNbtForSaving(BlockPos p_223134_1_) {
return null; return null;
} }
@Override @Override
public UpgradeData getUpgradeData() { public void setStartForFeature(ConfiguredStructureFeature<?, ?> structure, StructureStart start) {
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) {
} }
@Override @Override
@ -208,16 +124,16 @@ public class VirtualChunk extends ChunkAccess {
} }
@Override @Override
public void setAllStarts(Map<ConfiguredStructureFeature<?, ?>, StructureStart> pStructureStarts) { public void setAllStarts(Map<ConfiguredStructureFeature<?, ?>, StructureStart> structureStarts) {
} }
@Override @Override
public LongSet getReferencesForFeature(ConfiguredStructureFeature<?, ?> pStructure) { public LongSet getReferencesForFeature(ConfiguredStructureFeature<?, ?> structure) {
return LongSets.emptySet(); return LongSets.emptySet();
} }
@Override @Override
public void addReferenceForFeature(ConfiguredStructureFeature<?, ?> pStructure, long pReference) { public void addReferenceForFeature(ConfiguredStructureFeature<?, ?> structure, long reference) {
} }
@Override @Override
@ -226,17 +142,60 @@ public class VirtualChunk extends ChunkAccess {
} }
@Override @Override
public void setAllReferences(Map<ConfiguredStructureFeature<?, ?>, LongSet> pStructureReferences) { public void setAllReferences(Map<ConfiguredStructureFeature<?, ?>, LongSet> structureReferences) {
} }
@Override @Override
public int getHeight() { public void setUnsaved(boolean unsaved) {
return world.getHeight();
} }
@Override @Override
public int getMinBuildHeight() { public boolean isUnsaved() {
return world.getMinBuildHeight(); 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<BlockPos> 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<Block> getBlockTicks() {
return BlackholeTickAccess.emptyContainer();
} }
@Override @Override
@ -246,12 +205,41 @@ public class VirtualChunk extends ChunkAccess {
@Override @Override
public TicksToSave getTicksForSerialization() { public TicksToSave getTicksForSerialization() {
return null; throw new UnsupportedOperationException();
} }
@Override @Override
public TickContainerAccess<Block> getBlockTicks() { public long getInhabitedTime() {
return BlackholeTickAccess.emptyContainer(); 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);
}
} }

View file

@ -3,10 +3,10 @@ package com.jozufozu.flywheel.core.virtual;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.material.FluidState;
public class VirtualChunkSection extends LevelChunkSection { public class VirtualChunkSection extends LevelChunkSection {
public final VirtualChunk owner;
public VirtualChunk owner;
public final int xStart; public final int xStart;
public final int yStart; public final int yStart;
@ -32,8 +32,12 @@ public class VirtualChunkSection extends LevelChunkSection {
} }
@Override @Override
public BlockState setBlockState(int p_177484_1_, int p_177484_2_, int p_177484_3_, BlockState p_177484_4_, public FluidState getFluidState(int x, int y, int z) {
boolean p_177484_5_) { return getBlockState(x, y, z).getFluidState();
throw new IllegalStateException("Chunk sections should not be mutated in a fake world."); }
@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.");
} }
} }

View file

@ -1,62 +1,65 @@
package com.jozufozu.flywheel.core.virtual; package com.jozufozu.flywheel.core.virtual;
import java.util.HashMap;
import java.util.function.BooleanSupplier; 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.ChunkPos;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.lighting.LevelLightEngine;
public class VirtualChunkSource extends ChunkSource { public class VirtualChunkSource extends ChunkSource {
private final VirtualRenderWorld world; private final VirtualRenderWorld world;
private final Long2ObjectMap<VirtualChunk> chunks = new Long2ObjectOpenHashMap<>();
public final HashMap<Long, VirtualChunk> chunks = new HashMap<>();
public VirtualChunkSource(VirtualRenderWorld world) { public VirtualChunkSource(VirtualRenderWorld world) {
this.world = world; this.world = world;
} }
@Override
public BlockGetter getChunkForLighting(int x, int z) {
return getChunk(x, z);
}
@Override @Override
public Level getLevel() { public Level getLevel() {
return world; 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) { public ChunkAccess getChunk(int x, int z) {
long pos = ChunkPos.asLong(x, z); long pos = ChunkPos.asLong(x, z);
return chunks.computeIfAbsent(pos, $ -> new VirtualChunk(world, 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 @Override
public String gatherStats() { public String gatherStats() {
return "WrappedChunkProvider"; return "VirtualChunkSource";
}
@Override
public LevelLightEngine getLightEngine() {
return world.getLightEngine();
}
@Override
public void tick(BooleanSupplier p_202162_, boolean p_202163_) {
} }
@Override @Override
public int getLoadedChunksCount() { public int getLoadedChunksCount() {
return 0; return 0;
} }
@Override
public LevelLightEngine getLightEngine() {
return world.getLightEngine();
}
} }

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.core.virtual; package com.jozufozu.flywheel.core.virtual;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -32,6 +34,7 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
} }
@Override @Override
@Nullable
default BlockEntity getBlockEntity(BlockPos pos) { default BlockEntity getBlockEntity(BlockPos pos) {
return null; return null;
} }
@ -99,7 +102,7 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
} }
@Override @Override
public void onBlockEmissionIncrease(BlockPos pos, int p_164456_) { public void onBlockEmissionIncrease(BlockPos pos, int emissionLevel) {
} }
@Override @Override
@ -108,16 +111,16 @@ public interface VirtualEmptyBlockGetter extends BlockAndTintGetter {
} }
@Override @Override
public int runUpdates(int p_164449_, boolean p_164450_, boolean p_164451_) { public int runUpdates(int pos, boolean isQueueEmpty, boolean updateBlockLight) {
return p_164449_; return pos;
} }
@Override @Override
public void updateSectionStatus(SectionPos pos, boolean p_75838_) { public void updateSectionStatus(SectionPos pos, boolean isQueueEmpty) {
} }
@Override @Override
public void enableLightSources(ChunkPos pos, boolean p_164453_) { public void enableLightSources(ChunkPos pos, boolean isQueueEmpty) {
} }
@Override @Override

View file

@ -10,14 +10,13 @@ import net.minecraft.world.level.entity.LevelEntityGetter;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
public class VirtualLevelEntityGetter<T extends EntityAccess> implements LevelEntityGetter<T> { public class VirtualLevelEntityGetter<T extends EntityAccess> implements LevelEntityGetter<T> {
@Override @Override
public T get(int p_156931_) { public T get(int id) {
return null; return null;
} }
@Override @Override
public T get(UUID pUuid) { public T get(UUID uuid) {
return null; return null;
} }
@ -27,15 +26,14 @@ public class VirtualLevelEntityGetter<T extends EntityAccess> implements LevelEn
} }
@Override @Override
public <U extends T> void get(EntityTypeTest<T, U> p_156935_, Consumer<U> p_156936_) { public <U extends T> void get(EntityTypeTest<T, U> test, Consumer<U> consumer) {
} }
@Override @Override
public void get(AABB p_156937_, Consumer<T> p_156938_) { public void get(AABB boundingBox, Consumer<T> consumer) {
} }
@Override @Override
public <U extends T> void get(EntityTypeTest<T, U> p_156932_, AABB p_156933_, Consumer<U> p_156934_) { public <U extends T> void get(EntityTypeTest<T, U> test, AABB bounds, Consumer<U> consumer) {
} }
} }

View file

@ -3,16 +3,15 @@ package com.jozufozu.flywheel.core.virtual;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.FlywheelWorld; 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.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Holder; 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.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource; 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.chunk.LevelChunk;
import net.minecraft.world.level.entity.LevelEntityGetter; import net.minecraft.world.level.entity.LevelEntityGetter;
import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.Fluid; 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.saveddata.maps.MapItemSavedData;
import net.minecraft.world.level.storage.WritableLevelData; import net.minecraft.world.level.storage.WritableLevelData;
import net.minecraft.world.scores.Scoreboard; import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.ticks.LevelTickAccess; import net.minecraft.world.ticks.LevelTickAccess;
public class VirtualRenderWorld extends Level implements FlywheelWorld { public class VirtualRenderWorld extends Level implements FlywheelWorld {
public final Map<BlockPos, BlockState> blocksAdded = new HashMap<>();
public final Map<BlockPos, BlockEntity> besAdded = new HashMap<>();
public final Set<SectionPos> spannedSections = new HashSet<>();
private final BlockPos.MutableBlockPos scratch = new BlockPos.MutableBlockPos();
protected final Level level; protected final Level level;
protected final LevelLightEngine lighter;
protected final VirtualChunkSource chunkSource;
protected final LevelEntityGetter<Entity> entityGetter = new VirtualLevelEntityGetter<>();
protected final int height;
protected final int minBuildHeight; protected final int minBuildHeight;
protected final int height;
protected final Vec3i biomeOffset; protected final Vec3i biomeOffset;
protected final VirtualChunkSource chunkSource;
protected final LevelLightEngine lightEngine;
protected final Map<BlockPos, BlockState> blockStates = new HashMap<>();
protected final Map<BlockPos, BlockEntity> blockEntities = new HashMap<>();
protected final Object2ShortMap<SectionPos> nonEmptyBlockCounts = new Object2ShortOpenHashMap<>();
protected final LevelEntityGetter<Entity> entityGetter = new VirtualLevelEntityGetter<>();
protected final BlockPos.MutableBlockPos scratchPos = new BlockPos.MutableBlockPos();
public VirtualRenderWorld(Level level) { public VirtualRenderWorld(Level level) {
this(level, Vec3i.ZERO, level.getHeight(), level.getMinBuildHeight()); this(level, Vec3i.ZERO);
} }
public VirtualRenderWorld(Level level, Vec3i biomeOffset) { 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) { public VirtualRenderWorld(Level level, int minBuildHeight, int height, Vec3i biomeOffset) {
super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionTypeRegistration(), level::getProfiler, super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionTypeRegistration(), level.getProfilerSupplier(),
true, false, 0); true, false, 0);
this.biomeOffset = biomeOffset;
this.level = level; this.level = level;
this.height = nextMultipleOf16(height);
this.minBuildHeight = nextMultipleOf16(minBuildHeight); this.minBuildHeight = nextMultipleOf16(minBuildHeight);
this.height = nextMultipleOf16(height);
this.biomeOffset = biomeOffset;
this.chunkSource = new VirtualChunkSource(this); 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 {
} }
} }
/** public void clear() {
* Run this after you're done using setBlock(). blockStates.clear();
*/ blockEntities.clear();
public void runLightingEngine() {
for (Map.Entry<BlockPos, BlockState> entry : blocksAdded.entrySet()) {
BlockPos pos = entry.getKey();
BlockState state = entry.getValue();
int light = state.getLightEmission(this, pos);
if (light > 0) {
lighter.onBlockEmissionIncrease(pos, light);
}
}
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<BlockEntity> blockEntities) { public void setBlockEntities(Collection<BlockEntity> blockEntities) {
besAdded.clear(); this.blockEntities.clear();
blockEntities.forEach(be -> besAdded.put(be.getBlockPos(), be)); 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 // MEANINGFUL OVERRIDES
@Override @Override
public boolean setBlock(BlockPos pos, BlockState newState, int flags) { public LevelChunk getChunk(int x, int z) {
blocksAdded.put(pos, newState); 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); SectionPos sectionPos = SectionPos.of(pos);
if (spannedSections.add(sectionPos)) { short nonEmptyBlockCount = nonEmptyBlockCounts.getShort(sectionPos);
lighter.updateSectionStatus(sectionPos, false); 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) { if ((flags & Block.UPDATE_SUPPRESS_LIGHT) == 0) {
lighter.checkBlock(pos); lightEngine.checkBlock(pos);
} }
return true; return true;
} }
@Override
public int getHeight() {
return height;
}
@Override
public int getMinBuildHeight() {
return minBuildHeight;
}
@Override
public ChunkSource getChunkSource() {
return chunkSource;
}
@Override @Override
public LevelLightEngine getLightEngine() { 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 @Override
@ -158,52 +241,39 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
} }
@Override @Override
public BlockState getBlockState(@Nullable BlockPos pos) { public ChunkSource getChunkSource() {
BlockState state = blocksAdded.get(pos); return chunkSource;
if (state != null)
return state;
return Blocks.AIR.defaultBlockState();
} }
@Override @Override
public boolean setBlockAndUpdate(BlockPos pos, BlockState state) { public int getMinBuildHeight() {
return setBlock(pos, state, 0); return minBuildHeight;
} }
@Override @Override
@Nullable public int getHeight() {
public BlockEntity getBlockEntity(BlockPos pos) { return height;
return besAdded.get(pos);
}
@Override
public boolean isStateAtPosition(BlockPos pos, Predicate<BlockState> condition) {
return condition.test(getBlockState(pos));
}
public BlockState getBlockState(int x, int y, int z) {
return getBlockState(scratch.set(x, y, z));
} }
// BIOME OFFSET // BIOME OFFSET
@Override @Override
public Holder<Biome> getBiome(BlockPos pPos) { public Holder<Biome> getBiome(BlockPos pos) {
return super.getBiome(pPos.offset(biomeOffset)); return super.getBiome(pos.offset(biomeOffset));
} }
@Override @Override
public Holder<Biome> getUncachedNoiseBiome(int pX, int pY, int pZ) { public Holder<Biome> getNoiseBiome(int x, int y, int z) {
// Control flow should never reach this method, // Control flow should never reach this method,
// so we add biomeOffset in case some other mod calls this directly. // 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 @Override
public Holder<Biome> getNoiseBiome(int pX, int pY, int pZ) { public Holder<Biome> getUncachedNoiseBiome(int x, int y, int z) {
// Control flow should never reach this method, // Control flow should never reach this method,
// so we add biomeOffset in case some other mod calls this directly. // 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 // RENDERING CONSTANTS
@ -214,22 +284,27 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
} }
@Override @Override
public float getShade(Direction p_230487_1_, boolean p_230487_2_) { public float getShade(Direction direction, boolean shade) {
return 1f; return 1f;
} }
// THIN WRAPPERS AHEAD // THIN WRAPPERS
@Override
public Scoreboard getScoreboard() {
return level.getScoreboard();
}
@Override
public RecipeManager getRecipeManager() {
return level.getRecipeManager();
}
@Override @Override
public BiomeManager getBiomeManager() { public BiomeManager getBiomeManager() {
return level.getBiomeManager(); return level.getBiomeManager();
} }
@Override
public RegistryAccess registryAccess() {
return level.registryAccess();
}
@Override @Override
public LevelTickAccess<Block> getBlockTicks() { public LevelTickAccess<Block> getBlockTicks() {
return level.getBlockTicks(); return level.getBlockTicks();
@ -241,21 +316,44 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
} }
@Override @Override
public RecipeManager getRecipeManager() { public RegistryAccess registryAccess() {
return level.getRecipeManager(); return level.registryAccess();
}
// ADDITIONAL OVERRRIDES
@Override
public void updateNeighbourForOutputSignal(BlockPos pos, Block block) {
} }
@Override @Override
public int getFreeMapId() { public boolean isLoaded(BlockPos pos) {
return level.getFreeMapId(); return true;
} }
@Override @Override
public Scoreboard getScoreboard() { public boolean isAreaLoaded(BlockPos center, int range) {
return level.getScoreboard(); 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 @Override
@Nullable @Nullable
@ -270,13 +368,24 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
} }
@Override @Override
public boolean isLoaded(BlockPos pos) { public void setMapData(String mapId, MapItemSavedData data) {
return true;
} }
@Override @Override
public boolean isAreaLoaded(BlockPos center, int range) { public int getFreeMapId() {
return true; 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 @Override
@ -284,39 +393,6 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
return Collections.emptyList(); 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: // Override Starlight's ExtendedWorld interface methods:
public LevelChunk getChunkAtImmediately(final int chunkX, final int chunkZ) { public LevelChunk getChunkAtImmediately(final int chunkX, final int chunkZ) {