diff --git a/build.gradle b/build.gradle index 5b921c9d8..71de6ea8a 100644 --- a/build.gradle +++ b/build.gradle @@ -132,6 +132,7 @@ minecraft { "-Dcubicchunks.debug.window=false", "-Dcubicchunks.debug.statusrenderer=false", "-Dcubicchunks.debug.biomes=false", + "-ea" ] runConfigs { diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml index 9dbcc9723..d7668838e 100644 --- a/config/checkstyle/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -21,7 +21,7 @@ - + diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/CubeMap.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/CubeMap.java new file mode 100644 index 000000000..dc79692d5 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/CubeMap.java @@ -0,0 +1,24 @@ +package io.github.opencubicchunks.cubicchunks.chunk; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class CubeMap { + private final ConcurrentHashMap.KeySetView loadedCubes = ConcurrentHashMap.newKeySet(); + + public boolean isLoaded(int cubeY) { + return loadedCubes.contains(cubeY); + } + + public void markLoaded(int cubeY) { + loadedCubes.add(cubeY); + } + + public void markUnloaded(int cubeY) { + loadedCubes.remove(cubeY); + } + + public Set getLoaded() { + return loadedCubes; + } +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/CubeMapGetter.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/CubeMapGetter.java new file mode 100644 index 000000000..13db6dbe5 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/CubeMapGetter.java @@ -0,0 +1,5 @@ +package io.github.opencubicchunks.cubicchunks.chunk; + +public interface CubeMapGetter { + CubeMap getCubeMap(); +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/IBigCube.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/IBigCube.java index 9018b9e87..4a98f5bf5 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/IBigCube.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/IBigCube.java @@ -6,6 +6,7 @@ import javax.annotation.Nullable; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.LightSurfaceTrackerSection; import io.github.opencubicchunks.cubicchunks.chunk.heightmap.SurfaceTrackerSection; import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; import io.github.opencubicchunks.cubicchunks.meta.EarlyConfig; @@ -88,4 +89,7 @@ default void setCubeBlockEntity(CompoundTag nbt) { default void loadHeightmapSection(SurfaceTrackerSection section, int localSectionX, int localSectionZZ) { } + + default void setLightHeightmapSection(LightSurfaceTrackerSection section, int localSectionX, int localSectionZZ) { + } } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/IChunkManager.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/IChunkManager.java index 63f08ccec..3f13b6d4e 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/IChunkManager.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/IChunkManager.java @@ -30,6 +30,7 @@ public interface IChunkManager { @Nullable ChunkHolder updateCubeScheduling(long cubePosIn, int newLevel, @Nullable ChunkHolder holder, int oldLevel); + void setServerChunkCache(ServerChunkCache cache); LongSet getCubesToDrop(); @Nullable @@ -121,6 +122,4 @@ static int getCubeDistanceXZ(CubePos cubePosIn, int x, int z) { void releaseLightTicket(CubePos cubePos); boolean noPlayersCloseForSpawning(CubePos cubePos); - - void setServerChunkCache(ServerChunkCache cache); } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/ICubeHolder.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/ICubeHolder.java index e1c29e86d..723007991 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/ICubeHolder.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/ICubeHolder.java @@ -34,10 +34,6 @@ static ChunkStatus getCubeStatusFromLevel(int cubeLevel) { return cubeLevel < 33 ? ChunkStatus.FULL : CubeStatus.getStatus(cubeLevel - 33); } - void setChunkHolders(ChunkHolder[] chunkHolders); - - ChunkHolder[] getChunkHolders(); - @Nullable BigCube getCubeIfComplete(); diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/LightHeightmapGetter.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/LightHeightmapGetter.java new file mode 100644 index 000000000..bb226a025 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/LightHeightmapGetter.java @@ -0,0 +1,27 @@ +package io.github.opencubicchunks.cubicchunks.chunk; + +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.ClientLightSurfaceTracker; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.LightSurfaceTrackerWrapper; +import net.minecraft.world.level.levelgen.Heightmap; + +public interface LightHeightmapGetter { + Heightmap getLightHeightmap(); + + // Do not override this + default ClientLightSurfaceTracker getClientLightHeightmap() { + Heightmap lightHeightmap = this.getLightHeightmap(); + if (!(lightHeightmap instanceof ClientLightSurfaceTracker)) { + throw new IllegalStateException("Attempted to get client light heightmap on server"); + } + return (ClientLightSurfaceTracker) lightHeightmap; + } + + // Do not override this + default LightSurfaceTrackerWrapper getServerLightHeightmap() { + Heightmap lightHeightmap = this.getLightHeightmap(); + if (!(lightHeightmap instanceof LightSurfaceTrackerWrapper)) { + throw new IllegalStateException("Attempted to get server light heightmap on client"); + } + return (LightSurfaceTrackerWrapper) lightHeightmap; + } +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/cube/BigCube.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/cube/BigCube.java index 44a0ece07..ba6c3af41 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/cube/BigCube.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/cube/BigCube.java @@ -20,8 +20,11 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import io.github.opencubicchunks.cubicchunks.CubicChunks; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; import io.github.opencubicchunks.cubicchunks.chunk.ImposterChunkPos; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.LightSurfaceTrackerSection; import io.github.opencubicchunks.cubicchunks.chunk.heightmap.SurfaceTrackerSection; import io.github.opencubicchunks.cubicchunks.chunk.heightmap.SurfaceTrackerWrapper; import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; @@ -110,6 +113,7 @@ public String getType() { private final Map, LongSet> structuresRefences; private final Map heightmaps; + private final LightSurfaceTrackerSection[] lightHeightmaps = new LightSurfaceTrackerSection[4]; private ChunkBiomeContainer cubeBiomeContainer; @@ -218,10 +222,26 @@ public BigCube(Level worldIn, CubePrimer cubePrimer, @Nullable Consumer this.setAllReferences(cubePrimer.getAllReferences()); // var4 = protoChunk.getHeightmaps().iterator(); + LightSurfaceTrackerSection[] primerLightHeightmaps = cubePrimer.getLightHeightmaps(); + for (int i = 0; i < IBigCube.DIAMETER_IN_SECTIONS * IBigCube.DIAMETER_IN_SECTIONS; i++) { + this.lightHeightmaps[i] = primerLightHeightmaps[i]; + if (this.lightHeightmaps[i] == null) { + System.out.println("Got a null light heightmap while upgrading from CubePrimer at " + this.cubePos); + } else { + this.lightHeightmaps[i].upgradeCube(this); + } + } + this.setCubeLight(cubePrimer.hasCubeLight()); this.dirty = true; } + @Override public void setLightHeightmapSection(LightSurfaceTrackerSection section, int localSectionX, int localSectionZ) { + int idx = localSectionX + localSectionZ * DIAMETER_IN_SECTIONS; + + this.lightHeightmaps[idx] = section; + } + @Deprecated @Override public ChunkPos getPos() { throw new UnsupportedOperationException("Not implemented"); } @@ -983,14 +1003,20 @@ public void postLoad() { ChunkPos pos = this.cubePos.asChunkPos(); for (int x = 0; x < IBigCube.DIAMETER_IN_SECTIONS; x++) { for (int z = 0; z < IBigCube.DIAMETER_IN_SECTIONS; z++) { - // TODO we really, *really* shouldn't be force-loading columns here. - // probably the easiest approach until we get a "columns before cubes" invariant though. + + // TODO force-loading columns is questionable, until we get load order LevelChunk chunk = this.level.getChunk(pos.x + x, pos.z + z); + ((CubeMapGetter) chunk).getCubeMap().markLoaded(this.cubePos.getY()); for (Map.Entry entry : chunk.getHeightmaps()) { Heightmap heightmap = entry.getValue(); SurfaceTrackerWrapper tracker = (SurfaceTrackerWrapper) heightmap; tracker.loadCube(this); } + + if (!this.level.isClientSide) { + // TODO probably don't want to do this if the cube was already loaded as a CubePrimer + ((LightHeightmapGetter) chunk).getServerLightHeightmap().loadCube(this); + } } } } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/cube/CubePrimer.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/cube/CubePrimer.java index 5614c4178..3c6f75774 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/cube/CubePrimer.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/cube/CubePrimer.java @@ -1,5 +1,3 @@ - - package io.github.opencubicchunks.cubicchunks.chunk.cube; import static net.minecraft.world.level.chunk.LevelChunk.EMPTY_SECTION; @@ -19,14 +17,20 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; import io.github.opencubicchunks.cubicchunks.chunk.ImposterChunkPos; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; import io.github.opencubicchunks.cubicchunks.chunk.biome.CubeBiomeContainer; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.LightSurfaceTrackerSection; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.LightSurfaceTrackerWrapper; import io.github.opencubicchunks.cubicchunks.chunk.heightmap.SurfaceTrackerSection; import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; import io.github.opencubicchunks.cubicchunks.mixin.access.common.BiomeContainerAccess; import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; import io.github.opencubicchunks.cubicchunks.utils.Coords; +import io.github.opencubicchunks.cubicchunks.world.CubeWorldGenRegion; +import io.github.opencubicchunks.cubicchunks.world.lighting.ISkyLightColumnChecker; import io.github.opencubicchunks.cubicchunks.world.storage.CubeProtoTickList; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; @@ -35,7 +39,9 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.block.Block; @@ -44,6 +50,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkBiomeContainer; +import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.ProtoChunk; @@ -72,9 +79,10 @@ public class CubePrimer extends ProtoChunk implements IBigCube, CubicLevelHeight @Nullable private CubeBiomeContainer cubeBiomeContainer; - private final Map heightmaps; + private final LightSurfaceTrackerSection[] lightHeightmaps = new LightSurfaceTrackerSection[4]; + private final List entities = Lists.newArrayList(); private final Map tileEntities = Maps.newHashMap(); @@ -121,6 +129,7 @@ public CubePrimer(CubePos cubePosIn, UpgradeData upgradeData, @Nullable LevelChu this.carvingMasks = new Object2ObjectArrayMap<>(); this.structureStarts = Maps.newHashMap(); + this.structuresRefences = new ConcurrentHashMap<>(); // Maps.newHashMap(); //TODO: This should NOT be a ConcurrentHashMap this.cubePos = cubePosIn; @@ -135,6 +144,7 @@ public CubePrimer(CubePos cubePosIn, UpgradeData upgradeData, @Nullable LevelChu throw new IllegalStateException("Number of Sections must equal IBigCube.CUBESIZE | " + IBigCube.SECTION_COUNT); } } + isCubic = ((CubicLevelHeightAccessor) levelHeightAccessor).isCubic(); generates2DChunks = ((CubicLevelHeightAccessor) levelHeightAccessor).generates2DChunks(); worldStyle = ((CubicLevelHeightAccessor) levelHeightAccessor).worldStyle(); @@ -166,9 +176,67 @@ public void setHeightToCubeBounds(boolean cubeBounds) { return this.sections; } + private ChunkSource getChunkSource() { + if (this.levelHeightAccessor instanceof CubeWorldGenRegion) { + return ((CubeWorldGenRegion) this.levelHeightAccessor).getChunkSource(); + } else { + return ((ServerLevel) this.levelHeightAccessor).getChunkSource(); + } + } + //STATUS public void setCubeStatus(ChunkStatus newStatus) { this.status = newStatus; + + if (this.status == ChunkStatus.FEATURES) { + ChunkSource chunkSource = getChunkSource(); + + for (int dx = 0; dx < IBigCube.DIAMETER_IN_SECTIONS; dx++) { + for (int dz = 0; dz < IBigCube.DIAMETER_IN_SECTIONS; dz++) { + + // get the chunk for this section + ChunkPos chunkPos = this.cubePos.asChunkPos(dx, dz); + BlockGetter chunk = chunkSource.getChunkForLighting(chunkPos.x, chunkPos.z); + + // the load order guarantees the chunk being present + assert (chunk != null); + + ((CubeMapGetter) chunk).getCubeMap().markLoaded(this.cubePos.getY()); + + LightSurfaceTrackerWrapper lightHeightmap = ((LightHeightmapGetter) chunk).getServerLightHeightmap(); + + int[] beforeValues = new int[SECTION_DIAMETER * SECTION_DIAMETER]; + for (int z = 0; z < SECTION_DIAMETER; z++) { + for (int x = 0; x < SECTION_DIAMETER; x++) { + beforeValues[z * SECTION_DIAMETER + x] = lightHeightmap.getFirstAvailable(x, z); + } + } + + lightHeightmap.loadCube(this); + + for (int z = 0; z < SECTION_DIAMETER; z++) { + for (int x = 0; x < SECTION_DIAMETER; x++) { + int beforeValue = beforeValues[z * SECTION_DIAMETER + x]; + int afterValue = lightHeightmap.getFirstAvailable(x, z); + if (beforeValue != afterValue) { + ((ISkyLightColumnChecker) chunkSource.getLightEngine()).checkSkyLightColumn((CubeMapGetter) chunk, + chunkPos.getBlockX(x), chunkPos.getBlockZ(z), beforeValue, afterValue); + } + } + } + } + } + } + } + + @Override + public void setLightHeightmapSection(LightSurfaceTrackerSection section, int localSectionX, int localSectionZ) { + int idx = localSectionX + localSectionZ * DIAMETER_IN_SECTIONS; + this.lightHeightmaps[idx] = section; + } + + public LightSurfaceTrackerSection[] getLightHeightmaps() { + return lightHeightmaps; } @Override public ChunkStatus getCubeStatus() { @@ -176,68 +244,112 @@ public void setCubeStatus(ChunkStatus newStatus) { } @Override @Nullable public BlockState setBlock(BlockPos pos, BlockState state, boolean isMoving) { - int x = pos.getX() & 0xF; - int y = pos.getY() & 0xF; - int z = pos.getZ() & 0xF; - int index = Coords.blockToIndex(pos.getX(), pos.getY(), pos.getZ()); + int xSection = pos.getX() & 0xF; + int ySection = pos.getY() & 0xF; + int zSection = pos.getZ() & 0xF; + int sectionIdx = Coords.blockToIndex(pos.getX(), pos.getY(), pos.getZ()); - LevelChunkSection section = this.sections[index]; + LevelChunkSection section = this.sections[sectionIdx]; if (section == EMPTY_SECTION && state == EMPTY_BLOCK) { return state; } if (section == EMPTY_SECTION) { - section = new LevelChunkSection(Coords.cubeToMinBlock(this.cubePos.getY() + Coords.sectionToMinBlock(Coords.indexToY(index)))); - this.sections[index] = section; + section = new LevelChunkSection(Coords.cubeToMinBlock(this.cubePos.getY() + Coords.sectionToMinBlock(Coords.indexToY(sectionIdx)))); + this.sections[sectionIdx] = section; } if (state.getLightEmission() > 0) { - SectionPos sectionPosAtIndex = Coords.sectionPosByIndex(this.cubePos, index); + SectionPos sectionPosAtIndex = Coords.sectionPosByIndex(this.cubePos, sectionIdx); this.lightPositions.add(new BlockPos( - x + Coords.sectionToMinBlock(sectionPosAtIndex.getX()), - y + Coords.sectionToMinBlock(sectionPosAtIndex.getY()), - z + Coords.sectionToMinBlock(sectionPosAtIndex.getZ())) + xSection + Coords.sectionToMinBlock(sectionPosAtIndex.getX()), + ySection + Coords.sectionToMinBlock(sectionPosAtIndex.getY()), + zSection + Coords.sectionToMinBlock(sectionPosAtIndex.getZ())) ); } - BlockState lastState = section.setBlockState(x, y, z, state, false); - if (this.status.isOrAfter(ChunkStatus.FEATURES) && state != lastState && (state.getLightBlock(this, pos) != lastState.getLightBlock(this, pos) - || state.getLightEmission() != lastState.getLightEmission() || state.useShapeForLightOcclusion() || lastState.useShapeForLightOcclusion())) { + BlockState lastState = section.setBlockState(xSection, ySection, zSection, state, false); + if (this.status.isOrAfter(ChunkStatus.LIGHT) && state != lastState && (state.getLightBlock(this, pos) != lastState.getLightBlock(this, pos) + || state.getLightEmission() != lastState.getLightEmission() || state.useShapeForLightOcclusion() || lastState.useShapeForLightOcclusion())) { + + // get the chunk containing the updated block + ChunkSource chunkSource = getChunkSource(); + ChunkPos chunkPos = Coords.chunkPosByIndex(this.cubePos, sectionIdx); + BlockGetter chunk = chunkSource.getChunkForLighting(chunkPos.x, chunkPos.z); + + // the load order guarantees the chunk being present + assert (chunk != null); + + LightSurfaceTrackerWrapper lightHeightmap = ((LightHeightmapGetter) chunk).getServerLightHeightmap(); + + int relX = pos.getX() & 15; + int relZ = pos.getZ() & 15; + int oldHeight = lightHeightmap.getFirstAvailable(relX, relZ); + // Light heightmap update needs to occur before the light engine update. + // Not sure if this is the right blockstate to pass in, but it doesn't actually matter since we don't use it + lightHeightmap.update(relX, pos.getY(), relZ, state); + int newHeight = lightHeightmap.getFirstAvailable(relX, relZ); + if (newHeight != oldHeight) { + ((ISkyLightColumnChecker) chunkSource.getLightEngine()).checkSkyLightColumn((CubeMapGetter) chunk, pos.getX(), pos.getZ(), oldHeight, newHeight); + } lightManager.checkBlock(pos); } EnumSet heightMapsAfter = this.getStatus().heightmapsAfter(); - EnumSet toInitialize = null; - - for (Heightmap.Types type : heightMapsAfter) { - SurfaceTrackerSection[] heightmapArray = this.heightmaps.get(type); - - if (heightmapArray == null) { - if (toInitialize == null) { - toInitialize = EnumSet.noneOf(Heightmap.Types.class); - } +// EnumSet toInitialize = null; +// +// for (Heightmap.Types type : heightMapsAfter) { +// SurfaceTrackerSection[] heightmapArray = this.heightmaps.get(type); +// +// if (heightmapArray == null) { +// if (toInitialize == null) { +// toInitialize = EnumSet.noneOf(Heightmap.Types.class); +// } +// +// toInitialize.add(type); +// } +// } +// +// if (toInitialize != null) { +// primeHeightMaps(toInitialize); +// } + + int xChunk = Coords.blockToCubeLocalSection(pos.getX()); + int zChunk = Coords.blockToCubeLocalSection(pos.getZ()); + int chunkIdx = xChunk + zChunk * DIAMETER_IN_SECTIONS; - toInitialize.add(type); - } + for (Heightmap.Types types : heightMapsAfter) { + SurfaceTrackerSection surfaceTrackerSection = getHeightmapSections(types)[chunkIdx]; + surfaceTrackerSection.onSetBlock(xSection, pos.getY(), zSection, state); } - if (toInitialize != null) { - primeHeightMaps(toInitialize); - } + return lastState; + } - for (Heightmap.Types types : heightMapsAfter) { + /** + * Gets the SurfaceTrackerSections for the given Heightmap.Types for all chunks of this cube. + * Lazily initializes new SurfaceTrackerSections. + */ + private SurfaceTrackerSection[] getHeightmapSections(Heightmap.Types type) { + + SurfaceTrackerSection[] surfaceTrackerSections = heightmaps.get(type); - int xSection = Coords.blockToCubeLocalSection(pos.getX()); - int zSection = Coords.blockToCubeLocalSection(pos.getZ()); + if (surfaceTrackerSections == null) { + surfaceTrackerSections = new SurfaceTrackerSection[IBigCube.DIAMETER_IN_SECTIONS * IBigCube.DIAMETER_IN_SECTIONS]; - int idx = xSection + zSection * DIAMETER_IN_SECTIONS; + for (int dx = 0; dx < IBigCube.DIAMETER_IN_SECTIONS; dx++) { + for (int dz = 0; dz < IBigCube.DIAMETER_IN_SECTIONS; dz++) { + int idx = dx + dz * IBigCube.DIAMETER_IN_SECTIONS; + surfaceTrackerSections[idx] = new SurfaceTrackerSection(0, cubePos.getY(), null, this, type); + surfaceTrackerSections[idx].loadCube(dx, dz, this, true); + } + } - SurfaceTrackerSection surfaceTrackerSection = this.heightmaps.get(types)[idx]; - surfaceTrackerSection.onSetBlock(x, pos.getY(), z, state); + heightmaps.put(type, surfaceTrackerSections); } - return lastState; + return surfaceTrackerSections; } private void primeHeightMaps(EnumSet toInitialize) { @@ -383,7 +495,7 @@ public void setCubeLightManager(LevelLightEngine newLightEngine) { } @Override public int getCubeLocalHeight(Heightmap.Types type, int x, int z) { - SurfaceTrackerSection[] surfaceTrackerSections = this.heightmaps.get(type); + SurfaceTrackerSection[] surfaceTrackerSections = getHeightmapSections(type); if (surfaceTrackerSections == null) { primeHeightMaps(EnumSet.of(type)); surfaceTrackerSections = this.heightmaps.get(type); diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/graph/CCTicketType.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/graph/CCTicketType.java index 25de11d1a..cdaff358e 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/graph/CCTicketType.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/graph/CCTicketType.java @@ -12,6 +12,8 @@ public class CCTicketType { public static final TicketType CCLIGHT = create("light", Comparator.comparingLong(CubePos::asLong)); public static final TicketType CCUNKNOWN = create("unknown", Comparator.comparingLong(CubePos::asLong), 1); + public static final TicketType CCCOLUMN = create("column", Comparator.comparingLong(CubePos::asLong)); + public static TicketType create(String nameIn, Comparator comparator) { return TicketTypeAccess.createNew(nameIn, comparator, 0L); diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/ClientLightSurfaceTracker.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/ClientLightSurfaceTracker.java new file mode 100644 index 000000000..90ec7f10f --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/ClientLightSurfaceTracker.java @@ -0,0 +1,59 @@ +package io.github.opencubicchunks.cubicchunks.chunk.heightmap; + +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; +import io.github.opencubicchunks.cubicchunks.mixin.access.common.HeightmapAccess; +import io.github.opencubicchunks.cubicchunks.world.lighting.ISkyLightColumnChecker; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.BitStorage; +import net.minecraft.util.Mth; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +public class ClientLightSurfaceTracker extends ClientSurfaceTracker { + private final int bitsPerColumn; + private final int minHeight; + public ClientLightSurfaceTracker(ChunkAccess chunkAccess) { + // type shouldn't matter + super(chunkAccess, Types.WORLD_SURFACE); + bitsPerColumn = Mth.ceillog2(((HeightmapAccess) this).getChunk().getHeight() + 1); + minHeight = ((HeightmapAccess) this).getChunk().getMinBuildHeight(); + } + + @Override public boolean update(int x, int y, int z, BlockState blockState) { + throw new UnsupportedOperationException("ClientLightSurfaceTracker.update should never be called"); + } + + protected VoxelShape getShape(BlockState blockState, BlockPos pos, Direction facing) { + return blockState.canOcclude() && blockState.useShapeForLightOcclusion() ? blockState.getFaceOcclusionShape(((HeightmapAccess) this).getChunk(), pos, facing) : Shapes.empty(); + } + + private static int getIndex(int x, int z) { + return x + z * 16; + } + + public void setRawData(long[] heightmap, LevelChunk chunk) { + // We need to compare the old and new data here, hence the inefficiencies with making a new bitstorage + // TODO can this be optimized to operate on long[]s directly instead of making an extra BitStorage? + BitStorage storage = ((HeightmapAccess) this).getData(); + BitStorage oldStorage = new BitStorage(bitsPerColumn, 256, storage.getRaw().clone()); + System.arraycopy(heightmap, 0, storage.getRaw(), 0, heightmap.length); +// ChunkAccess chunk = ((HeightmapAccess) this).getChunk(); + int baseX = chunk.getPos().getMinBlockX(); + int baseZ = chunk.getPos().getMinBlockZ(); + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + int index = getIndex(x, z); + int oldHeight = oldStorage.get(index) + minHeight; + int newHeight = storage.get(index) + minHeight; + if (oldHeight != newHeight) { + ((ISkyLightColumnChecker) Minecraft.getInstance().level.getLightEngine()).checkSkyLightColumn((CubeMapGetter) chunk, baseX + x, baseZ + z, oldHeight, newHeight); + } + } + } + } +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/ClientSurfaceTracker.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/ClientSurfaceTracker.java index b17e6ed0b..73f545b30 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/ClientSurfaceTracker.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/ClientSurfaceTracker.java @@ -10,13 +10,18 @@ public class ClientSurfaceTracker extends Heightmap { - private final Predicate isOpaque; + protected final Predicate isOpaque; public ClientSurfaceTracker(ChunkAccess chunkAccess, Types types) { super(chunkAccess, types); this.isOpaque = ((HeightmapAccess) this).getIsOpaque(); } + /** + * @param x column-local x + * @param y global y + * @param z column-local z + */ @Override public boolean update(int x, int y, int z, BlockState blockState) { int previous = getFirstAvailable(x, z); if (y <= previous - 2) { diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/LightSurfaceTrackerSection.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/LightSurfaceTrackerSection.java new file mode 100644 index 000000000..badf550d5 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/LightSurfaceTrackerSection.java @@ -0,0 +1,168 @@ +package io.github.opencubicchunks.cubicchunks.chunk.heightmap; + +import java.util.Arrays; + +import javax.annotation.Nullable; + +import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; +import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +public class LightSurfaceTrackerSection extends SurfaceTrackerSection { + public LightSurfaceTrackerSection() { + this(MAX_SCALE, 0, null); + } + + public LightSurfaceTrackerSection(int scale, int scaledY, SurfaceTrackerSection parent) { + // type shouldn't actually matter here + super(scale, scaledY, parent, Heightmap.Types.WORLD_SURFACE); + } + + public LightSurfaceTrackerSection(int scale, int scaledY, SurfaceTrackerSection parent, IBigCube cube) { + super(scale, scaledY, parent, cube, Heightmap.Types.WORLD_SURFACE); + } + + private LightSurfaceTrackerSection getRoot() { + SurfaceTrackerSection section = this; + while (section.parent != null) { + section = section.parent; + } + return (LightSurfaceTrackerSection) section; + } + + @Nullable + @Override + protected SurfaceTrackerSection loadNode(int newScaledY, int sectionScale, IBigCube newCube, boolean create) { + // TODO: loading from disk + if (!create) { + return null; + } + if (sectionScale == 0) { + return new LightSurfaceTrackerSection(sectionScale, newScaledY, this, newCube); + } + return new LightSurfaceTrackerSection(sectionScale, newScaledY, this); + } + + @Nullable + private LightSurfaceTrackerSection getSectionAbove() { + if (scale != 0) { + throw new IllegalStateException("Attempted to get section above for a non-zero scale section"); + } + // TODO this can be optimized - don't need to go to the root every time, just the lowest node that is a parent of both this node and the node above. + return (LightSurfaceTrackerSection) this.getRoot().getCubeNode(scaledY + 1); + } + + @Override + public int getHeight(int x, int z) { + int idx = index(x, z); + if (!isDirty(idx)) { + int relativeY = heights.get(idx); + return relToAbsY(relativeY, scaledY, scale); + } + + synchronized (this) { + int maxY = Integer.MIN_VALUE; + if (scale == 0) { + IBigCube cube = (IBigCube) cubeOrNodes; + CubePos cubePos = cube.getCubePos(); + + LightSurfaceTrackerSection sectionAbove = this.getSectionAbove(); + + int dy = IBigCube.DIAMETER_IN_BLOCKS - 1; + + // TODO unknown behavior for occlusion on a loading boundary (i.e. sectionAbove == null) + BlockState above = sectionAbove == null ? Blocks.AIR.defaultBlockState() : ((IBigCube) sectionAbove.cubeOrNodes).getBlockState(x, 0, z); + BlockState state = cube.getBlockState(x, dy, z); + + // note that this BlockPos relies on `cubePos.blockY` returning correct results when the local coord is not inside the cube + VoxelShape voxelShapeAbove = sectionAbove == null + ? Shapes.empty() + : this.getShape(above, new BlockPos(cubePos.blockX(x), cubePos.blockY(dy + 1), cubePos.blockZ(z)), Direction.DOWN); + VoxelShape voxelShape = this.getShape(state, new BlockPos(cubePos.blockX(x), cubePos.blockY(dy), cubePos.blockZ(z)), Direction.UP); + + while (dy >= 0) { + int lightBlock = state.getLightBlock(cube, new BlockPos(cubePos.blockX(x), cubePos.blockY(dy), cubePos.blockZ(z))); + if (lightBlock > 0 || Shapes.faceShapeOccludes(voxelShapeAbove, voxelShape)) { + int minY = scaledY * IBigCube.DIAMETER_IN_BLOCKS; + maxY = minY + dy; + break; + } + dy--; + if (dy >= 0) { + above = state; + state = cube.getBlockState(x, dy, z); + voxelShapeAbove = this.getShape(above, new BlockPos(cubePos.blockX(x), cubePos.blockY(dy + 1), cubePos.blockZ(z)), Direction.DOWN); + voxelShape = this.getShape(state, new BlockPos(cubePos.blockX(x), cubePos.blockY(dy), cubePos.blockZ(z)), Direction.UP); + } + } + } else { + SurfaceTrackerSection[] nodes = (SurfaceTrackerSection[]) cubeOrNodes; + for (int i = nodes.length - 1; i >= 0; i--) { + SurfaceTrackerSection node = nodes[i]; + if (node == null) { + continue; + } + int y = node.getHeight(x, z); + if (y != Integer.MIN_VALUE) { + maxY = y; + break; + } + } + } + heights.set(idx, absToRelY(maxY, scaledY, scale)); + clearDirty(idx); + return maxY; + } + } + + /** + * Used when upgrading CubePrimers to BigCubes; should never be used elsewhere. + */ + public void upgradeCube(IBigCube cube) { + if (this.scale != 0) { + throw new IllegalStateException("Attempted to upgrade the cube on a non-zero scale section"); + } + if (this.cubeOrNodes == null) { + throw new IllegalStateException("Attempting to upgrade cube " + cube.getCubePos() + " for an unloaded surface tracker section"); + } + this.cubeOrNodes = cube; + + } + + @Override + public void loadCube(int sectionX, int sectionZ, IBigCube newCube, boolean markDirty) { + if (this.cubeOrNodes == null) { + throw new IllegalStateException("Attempting to load cube " + newCube.getCubePos() + " into an unloaded surface tracker section"); + } + if (markDirty) { + Arrays.fill(dirtyPositions, -1); + } + if (this.scale == 0) { + // TODO merge loadHeightmapSection and loadLightHeightmapSection, and just use instanceof checks in the implementation to figure out if it's a light heightmap? + newCube.setLightHeightmapSection(this, sectionX, sectionZ); + return; + } + int idx = indexOfRawHeightNode(newCube.getCubePos().getY(), scale, scaledY); + SurfaceTrackerSection[] nodes = (SurfaceTrackerSection[]) cubeOrNodes; + for (int i = 0; i < nodes.length; i++) { + if (nodes[i] != null) { + continue; + } + int newScaledY = indexToScaledY(i, scale, scaledY); + SurfaceTrackerSection newMap = loadNode(newScaledY, scale - 1, newCube, i == idx); + nodes[i] = newMap; + } + assert nodes[idx] != null; + nodes[idx].loadCube(sectionX, sectionZ, newCube, markDirty); + } + + protected VoxelShape getShape(BlockState blockState, BlockPos pos, Direction facing) { + return blockState.canOcclude() && blockState.useShapeForLightOcclusion() ? blockState.getFaceOcclusionShape((IBigCube) this.cubeOrNodes, pos, facing) : Shapes.empty(); + } +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/LightSurfaceTrackerWrapper.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/LightSurfaceTrackerWrapper.java new file mode 100644 index 000000000..ed6d954c5 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/LightSurfaceTrackerWrapper.java @@ -0,0 +1,35 @@ +package io.github.opencubicchunks.cubicchunks.chunk.heightmap; + +import static io.github.opencubicchunks.cubicchunks.utils.Coords.*; + +import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; + +public class LightSurfaceTrackerWrapper extends SurfaceTrackerWrapper { + public LightSurfaceTrackerWrapper(ChunkAccess chunkAccess) { + // type shouldn't matter + super(chunkAccess, Types.WORLD_SURFACE, new LightSurfaceTrackerSection()); + } + + @Override + public boolean update(int x, int y, int z, BlockState blockState) { + super.update(x, y, z, blockState); + int relY = blockToLocal(y); + // TODO how are we going to handle making sure that unloaded sections stay updated? + if (relY == 0) { + SurfaceTrackerSection section = surfaceTracker.getCubeNode(blockToCube(y - 1)); + if (section != null) { + section.markDirty(x, z); + } + } else if (relY == IBigCube.DIAMETER_IN_BLOCKS - 1) { + SurfaceTrackerSection section = surfaceTracker.getCubeNode(blockToCube(y + 1)); + if (section != null) { + section.markDirty(x, z); + } + } + + // Return value is unused + return false; + } +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/SurfaceTrackerSection.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/SurfaceTrackerSection.java index 0698c5ef9..de7f3392b 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/SurfaceTrackerSection.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/SurfaceTrackerSection.java @@ -22,23 +22,23 @@ public class SurfaceTrackerSection { /** Number of children nodes */ public static final int NODE_COUNT = 1 << NODE_COUNT_BITS; + // Use width of 16 to match columns. + public static final int WIDTH_BLOCKS = 16; + private static final Heightmap.Types[] HEIGHTMAP_TYPES = Heightmap.Types.values(); /** Number of bits needed to represent height (excluding null) at scale zero (i.e. log2(scale0 height)) */ private static final int BASE_SIZE_BITS = IBigCube.SIZE_BITS; - // Use width of 16 to match columns. - private static final int WIDTH_BLOCKS = 16; - - private final BitStorage heights; - private final long[] dirtyPositions; // bitset has 100% memory usage overhead due to pointers and object headers - private SurfaceTrackerSection parent; - private Object cubeOrNodes; + protected final BitStorage heights; + protected final long[] dirtyPositions; // bitset has 100% memory usage overhead due to pointers and object headers + protected SurfaceTrackerSection parent; + protected Object cubeOrNodes; /** * Position of this section, within all sections of this size e.g. with 64-block sections, y=0-63 would be section 0, y=64-127 would be section 1, etc. */ - private final int scaledY; - private final byte scale; + protected final int scaledY; + protected final byte scale; private final byte heightmapType; public SurfaceTrackerSection(Heightmap.Types types) { @@ -61,7 +61,10 @@ public SurfaceTrackerSection(int scale, int scaledY, SurfaceTrackerSection paren this.heightmapType = (byte) types.ordinal(); } - /** Get the height for a given position. Recomputes the height if the column is marked dirty in this section. */ + /** + * Get the height for a given position. Recomputes the height if the column is marked dirty in this section. + * x and z are global coordinates. + */ public int getHeight(int x, int z) { int idx = index(x, z); if (!isDirty(idx)) { @@ -101,7 +104,7 @@ public int getHeight(int x, int z) { } } - private void clearDirty(int idx) { + protected void clearDirty(int idx) { dirtyPositions[idx >> 6] &= ~(1L << idx); } @@ -109,7 +112,7 @@ private void setDirty(int idx) { dirtyPositions[idx >> 6] |= 1L << idx; } - private boolean isDirty(int idx) { + protected boolean isDirty(int idx) { return (dirtyPositions[idx >> 6] & (1L << idx)) != 0; } @@ -241,7 +244,7 @@ public Heightmap.Types getType() { } @Nullable - private SurfaceTrackerSection loadNode(int newScaledY, int sectionScale, IBigCube newCube, boolean create) { + protected SurfaceTrackerSection loadNode(int newScaledY, int sectionScale, IBigCube newCube, boolean create) { // TODO: loading from disk if (!create) { return null; @@ -252,7 +255,8 @@ private SurfaceTrackerSection loadNode(int newScaledY, int sectionScale, IBigCub return new SurfaceTrackerSection(sectionScale, newScaledY, this, HEIGHTMAP_TYPES[this.heightmapType]); } - private int index(int x, int z) { + /** Get position x/z index within a column, from global/local pos */ + protected int index(int x, int z) { return (z & 0xF) * WIDTH_BLOCKS + (x & 0xF); } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/SurfaceTrackerWrapper.java b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/SurfaceTrackerWrapper.java index 0f0a5cd79..fd1b4074a 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/SurfaceTrackerWrapper.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/chunk/heightmap/SurfaceTrackerWrapper.java @@ -10,9 +10,11 @@ import net.minecraft.world.level.levelgen.Heightmap; public class SurfaceTrackerWrapper extends Heightmap { - private final SurfaceTrackerSection surfaceTracker; - private final int dx; - private final int dz; + protected final SurfaceTrackerSection surfaceTracker; + /** global x of min block in column */ + protected final int dx; + /** global z of min block in column */ + protected final int dz; public SurfaceTrackerWrapper(ChunkAccess chunkAccess, Types types) { super(chunkAccess, types); @@ -22,14 +24,30 @@ public SurfaceTrackerWrapper(ChunkAccess chunkAccess, Types types) { this.dz = sectionToMinBlock(chunkAccess.getPos().z); } + protected SurfaceTrackerWrapper(ChunkAccess chunkAccess, Types types, SurfaceTrackerSection root) { + super(chunkAccess, types); + ((HeightmapAccess) this).setIsOpaque(null); + this.surfaceTracker = root; + this.dx = sectionToMinBlock(chunkAccess.getPos().x); + this.dz = sectionToMinBlock(chunkAccess.getPos().z); + } + + /** + * + * @param x column-local x + * @param y global y + * @param z column-local z + * @param blockState unused. + * @return currently unused; always false + */ @Override public boolean update(int x, int y, int z, BlockState blockState) { - // TODO do we need to do anything else here? surfaceTracker.getCubeNode(blockToCube(y)).markDirty(x + dx, z + dz); - // TODO not sure if this is safe to do or if things depend on the result + // We always return false, because the result is never used anywhere anyway (by either vanilla or us) return false; } + /** x/z are column-local. */ @Override public int getFirstAvailable(int x, int z) { return surfaceTracker.getHeight(x + dx, z + dz) + 1; diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/ChunkManagerAccess.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/ChunkManagerAccess.java index bdb0e4d09..084eb2edf 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/ChunkManagerAccess.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/ChunkManagerAccess.java @@ -16,4 +16,6 @@ public interface ChunkManagerAccess { @Invoker boolean invokePromoteChunkMap(); @Invoker void invokeOnFullChunkStatusChange(ChunkPos chunkPos, ChunkHolder.FullChunkStatus fullChunkStatus); @Accessor int getViewDistance(); + + @Invoker void invokeReleaseLightTicket(ChunkPos pos); } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/SectionLightStorageAccess.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/LayerLightSectionStorageAccess.java similarity index 53% rename from src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/SectionLightStorageAccess.java rename to src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/LayerLightSectionStorageAccess.java index c9e10aa4c..2c86a1e28 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/SectionLightStorageAccess.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/LayerLightSectionStorageAccess.java @@ -1,12 +1,19 @@ package io.github.opencubicchunks.cubicchunks.mixin.access.common; +import net.minecraft.world.level.chunk.LightChunkGetter; import net.minecraft.world.level.lighting.LayerLightSectionStorage; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(LayerLightSectionStorage.class) -public interface SectionLightStorageAccess { +public interface LayerLightSectionStorageAccess { @Invoker("enableLightSources") void invokeSetColumnEnabled(long seed, boolean enable); + @Invoker boolean invokeStoringLightForSection(long sectionPos); + + @Invoker void invokeRunAllUpdates(); + + @Accessor LightChunkGetter getChunkSource(); } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/ThreadedLevelLightEngineAccess.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/ThreadedLevelLightEngineAccess.java new file mode 100644 index 000000000..1e1129473 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/ThreadedLevelLightEngineAccess.java @@ -0,0 +1,14 @@ +package io.github.opencubicchunks.cubicchunks.mixin.access.common; + +import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ThreadedLevelLightEngine; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(ThreadedLevelLightEngine.class) +public interface ThreadedLevelLightEngineAccess { + @Accessor ChunkMap getChunkMap(); + + @Invoker void invokeAddTask(int x, int z, ThreadedLevelLightEngine.TaskType stage, Runnable task); +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/TicketAccess.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/TicketAccess.java index 57c76a5d3..de494ca2b 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/TicketAccess.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/TicketAccess.java @@ -3,6 +3,7 @@ import net.minecraft.server.level.Ticket; import net.minecraft.server.level.TicketType; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Invoker; @Mixin(Ticket.class) @@ -12,4 +13,6 @@ public interface TicketAccess { } @Invoker boolean invokeTimedOut(long currentTime); @Invoker void invokeSetCreatedTick(long time); + + @Accessor T getKey(); } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/TicketManagerAccess.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/TicketManagerAccess.java index c1a7196c3..fd8efef53 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/TicketManagerAccess.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/access/common/TicketManagerAccess.java @@ -2,6 +2,7 @@ import net.minecraft.server.level.DistanceManager; import net.minecraft.server.level.Ticket; +import net.minecraft.util.SortedArraySet; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; @@ -10,4 +11,7 @@ public interface TicketManagerAccess { @Invoker void invokeAddTicket(long chunkPosIn, Ticket ticketIn); @Invoker void invokeUpdatePlayerTickets(int viewDistance); + + @Invoker SortedArraySet> invokeGetTickets(long position); + @Invoker void invokeRemoveTicket(long pos, Ticket ticket); } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/client/debug/MixinDebugScreenOverlay.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/client/debug/MixinDebugScreenOverlay.java new file mode 100644 index 000000000..3fa35497b --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/client/debug/MixinDebugScreenOverlay.java @@ -0,0 +1,57 @@ +package io.github.opencubicchunks.cubicchunks.mixin.core.client.debug; + +import java.util.List; + +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.LightSurfaceTrackerWrapper; +import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; +import it.unimi.dsi.fastutil.longs.LongSet; +import net.minecraft.client.gui.components.DebugScreenOverlay; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.lighting.LevelLightEngine; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(DebugScreenOverlay.class) +public abstract class MixinDebugScreenOverlay { + @Shadow abstract LevelChunk getServerChunk(); + + @Inject(method = "getGameInformation", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/client/multiplayer/ClientLevel;getBrightness(Lnet/minecraft/world/level/LightLayer;Lnet/minecraft/core/BlockPos;)I", + ordinal = 0), + locals = LocalCapture.CAPTURE_FAILHARD) + private void onGetGameInformation(CallbackInfoReturnable> cir, String string2, BlockPos pos, Entity entity, Direction direction, + String string7, Level level, LongSet longSet, List list, LevelChunk clientChunk, int i) { + LevelChunk serverChunk = this.getServerChunk(); + if (((CubicLevelHeightAccessor) level).isCubic()) { + String serverHeight = "???"; + if (serverChunk != null) { + LightSurfaceTrackerWrapper heightmap = ((LightHeightmapGetter) serverChunk).getServerLightHeightmap(); + int height = heightmap.getFirstAvailable(pos.getX() & 0xF, pos.getZ() & 0xF); + serverHeight = "" + height; + } + list.add("Server light heightmap height: " + serverHeight); + int clientHeight = ((LightHeightmapGetter) clientChunk).getClientLightHeightmap().getFirstAvailable(pos.getX() & 0xF, pos.getZ() & 0xF); + list.add("Client light heightmap height: " + clientHeight); + } + + // No cubic check here because it's a vanilla feature that was removed anyway + if (serverChunk != null) { + LevelLightEngine lightingProvider = level.getChunkSource().getLightEngine(); + list.add("Server Light: (" + lightingProvider.getLayerListener(LightLayer.SKY).getLightValue(pos) + " sky, " + + lightingProvider.getLayerListener(LightLayer.BLOCK).getLightValue(pos) + " block)"); + } else { + list.add("Server Light: (?? sky, ?? block)"); + } + } +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/MixinMinecraftServer.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/MixinMinecraftServer.java index 6d4f4dbd3..02c29b297 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/MixinMinecraftServer.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/MixinMinecraftServer.java @@ -122,10 +122,10 @@ private void prepareLevels(ChunkProgressListener statusListener, CallbackInfo ci int chunkDiameter = Coords.cubeToSection(radius, 0) * 2 + 1; int d = radius * 2 + 1; ((IServerChunkProvider) serverchunkprovider).addCubeRegionTicket(TicketType.START, spawnPosCube, radius + 1, Unit.INSTANCE); - serverchunkprovider.addRegionTicket(TicketType.START, spawnPosCube.asChunkPos(), Coords.cubeToSection(radius + 1, 0), Unit.INSTANCE); +// serverchunkprovider.addRegionTicket(TicketType.START, spawnPosCube.asChunkPos(), Coords.cubeToSection(radius + 1, 0), Unit.INSTANCE); - while (this.isRunning() && (serverchunkprovider.getTickingGenerated() < chunkDiameter * chunkDiameter - || ((IServerChunkProvider) serverchunkprovider).getTickingGeneratedCubes() < d * d * d)) { + while (this.isRunning() && (/*serverchunkprovider.getTickingGenerated() < chunkDiameter * chunkDiameter + ||*/ ((IServerChunkProvider) serverchunkprovider).getTickingGeneratedCubes() < d * d * d)) { // from CC this.nextTickTime = Util.getMillis() + 10L; this.waitUntilNextTick(); diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/MixinServerChunkProvider.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/MixinServerChunkProvider.java index 197653bd0..ed8bb7ca5 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/MixinServerChunkProvider.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/MixinServerChunkProvider.java @@ -89,30 +89,7 @@ public abstract class MixinServerChunkProvider implements IServerChunkProvider, @Shadow @org.jetbrains.annotations.Nullable protected abstract ChunkHolder getVisibleChunkIfPresent(long pos); - @Inject(method = "", at = @At("RETURN")) - private void onInit(ServerLevel serverLevel, LevelStorageSource.LevelStorageAccess levelStorageAccess, DataFixer dataFixer, StructureManager structureManager, Executor executor, - ChunkGenerator chunkGenerator, int i, boolean bl, ChunkProgressListener chunkProgressListener, ChunkStatusUpdateListener chunkStatusUpdateListener, - Supplier supplier, CallbackInfo ci) { - ((IChunkManager) chunkMap).setServerChunkCache((ServerChunkCache) (Object) this); - } - - @Override public ChunkHolder getChunkHolderForce(ChunkPos chunkPos, ChunkStatus requiredStatus) { - long pos = chunkPos.toLong(); - ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(pos); - int chunkLevel = 33 + ChunkStatus.getDistance(requiredStatus); - this.distanceManager.addTicket(TicketType.UNKNOWN, chunkPos, chunkLevel, chunkPos); - if (this.chunkAbsent(chunkHolder, chunkLevel)) { - ProfilerFiller profilerFiller = this.level.getProfiler(); - profilerFiller.push("chunkLoad"); - this.runChunkDistanceManagerUpdates(); - chunkHolder = this.getVisibleChunkIfPresent(pos); - profilerFiller.pop(); - if (this.chunkAbsent(chunkHolder, chunkLevel)) { - throw Util.pauseInIde(new IllegalStateException("No chunk holder after ticket has been added")); - } - } - return chunkHolder; - } + @Shadow abstract boolean runDistanceManagerUpdates(); @Override public void addCubeRegionTicket(TicketType type, CubePos pos, int distance, T value) { @@ -208,6 +185,29 @@ public void forceCube(CubePos pos, boolean add) { ((ITicketManager) this.distanceManager).updateCubeForced(pos, add); } + + @Override public CompletableFuture> getColumnFutureForCube(CubePos cubepos, int chunkX, int chunkZ, ChunkStatus leastStatus, + boolean create) { + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); + long l = chunkPos.toLong(); + int i = 33 + ChunkStatus.getDistance(leastStatus); + ChunkHolder chunkHolder = this.getVisibleChunkIfPresent(l); + if (create) { + this.distanceManager.addTicket(CCTicketType.CCCOLUMN, chunkPos, i, cubepos); + if (this.chunkAbsent(chunkHolder, i)) { + ProfilerFiller profilerFiller = this.level.getProfiler(); + profilerFiller.push("chunkLoad"); + this.runDistanceManagerUpdates(); + chunkHolder = this.getVisibleChunkIfPresent(l); + profilerFiller.pop(); + if (this.chunkAbsent(chunkHolder, i)) { + throw Util.pauseInIde(new IllegalStateException("No chunk holder after ticket has been added")); + } + } + } + return this.chunkAbsent(chunkHolder, i) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : chunkHolder.getOrScheduleFuture(leastStatus, this.chunkMap); + } + // func_217233_c, getChunkFutureMainThread private CompletableFuture> getCubeFutureMainThread(int cubeX, int cubeY, int cubeZ, ChunkStatus requiredStatus, boolean load) { @@ -265,17 +265,6 @@ private void onRefreshAndInvalidate(CallbackInfoReturnable cir) { this.clearCubeCache(); } - private boolean runChunkDistanceManagerUpdates() { - boolean flag = ((ITicketManager) this.distanceManager).runAllUpdatesForChunks(chunkMap); - boolean flag1 = ((ChunkManagerAccess) this.chunkMap).invokePromoteChunkMap(); - if (!flag && !flag1) { - return false; - } else { - this.clearCubeCache(); - return true; - } - } - // func_217235_l, runDistanceManagerUpdates private boolean runCubeDistanceManagerUpdates() { boolean flag = this.distanceManager.runAllUpdates(this.chunkMap); @@ -320,6 +309,12 @@ public BlockGetter getCubeForLighting(int sectionX, int sectionY, int sectionZ) } } + @Inject(method = "", at = @At(value = "RETURN")) + private void setChunkManagerServerChunkCache(ServerLevel serverLevel, LevelStorageSource.LevelStorageAccess levelStorageAccess, DataFixer dataFixer, StructureManager structureManager, + Executor executor, ChunkGenerator chunkGenerator, int i, boolean bl, ChunkProgressListener chunkProgressListener, + ChunkStatusUpdateListener chunkStatusUpdateListener, Supplier supplier, CallbackInfo ci) { + ((IChunkManager) this.chunkMap).setServerChunkCache((ServerChunkCache) (Object) this); + } /** * @author Barteks2x * @reason sections diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunk.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunk.java index 0808a5f0e..a23c34ca5 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunk.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunk.java @@ -4,18 +4,25 @@ import java.util.function.Consumer; import io.github.opencubicchunks.cubicchunks.chunk.ChunkCubeGetter; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMap; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; import io.github.opencubicchunks.cubicchunks.chunk.ICubeProvider; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; import io.github.opencubicchunks.cubicchunks.chunk.biome.ColumnBiomeContainer; import io.github.opencubicchunks.cubicchunks.chunk.cube.BigCube; import io.github.opencubicchunks.cubicchunks.chunk.cube.EmptyCube; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.ClientLightSurfaceTracker; import io.github.opencubicchunks.cubicchunks.chunk.heightmap.ClientSurfaceTracker; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.LightSurfaceTrackerWrapper; import io.github.opencubicchunks.cubicchunks.chunk.heightmap.SurfaceTrackerWrapper; import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; import io.github.opencubicchunks.cubicchunks.utils.Coords; +import io.github.opencubicchunks.cubicchunks.world.lighting.ISkyLightColumnChecker; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.TickList; @@ -27,6 +34,7 @@ import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.material.Fluid; @@ -39,9 +47,11 @@ import org.spongepowered.asm.mixin.injection.ModifyConstant; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(value = LevelChunk.class, priority = 0) // Priority 0 to always ensure our redirects are on top. Should also prevent fabric api crashes that have occur(ed) here. See removeTileEntity -public abstract class MixinChunk implements ChunkAccess, CubicLevelHeightAccessor, ChunkCubeGetter { +@Mixin(value = LevelChunk.class, priority = 0) //Priority 0 to always ensure our redirects are on top. Should also prevent fabric api crashes that have occur(ed) here. See removeTileEntity + +public abstract class MixinChunk implements ChunkAccess, LightHeightmapGetter, CubeMapGetter, CubicLevelHeightAccessor, ChunkCubeGetter { @Shadow @Final private Level level; @Shadow @Final private ChunkPos chunkPos; @@ -54,6 +64,9 @@ public abstract class MixinChunk implements ChunkAccess, CubicLevelHeightAccesso private boolean generates2DChunks; private WorldStyle worldStyle; + private Heightmap lightHeightmap; + private CubeMap cubeMap; + @Shadow public abstract ChunkStatus getStatus(); @Shadow protected abstract boolean isInLevel(); @@ -64,6 +77,19 @@ public abstract class MixinChunk implements ChunkAccess, CubicLevelHeightAccesso return false; } + @Override + public Heightmap getLightHeightmap() { + if (!isCubic) { + throw new UnsupportedOperationException("Attempted to get light heightmap on a non-cubic chunk"); + } + return lightHeightmap; + } + + @Override + public CubeMap getCubeMap() { + return cubeMap; + } + @Inject( method = "(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/chunk/ChunkBiomeContainer;" + "Lnet/minecraft/world/level/chunk/UpgradeData;Lnet/minecraft/world/level/TickList;Lnet/minecraft/world/level/TickList;J[Lnet/minecraft/world/level/chunk/LevelChunkSection;" @@ -84,6 +110,52 @@ private void onInit(Level levelIn, ChunkPos pos, ChunkBiomeContainer chunkBiomeC if (!(biomes instanceof ColumnBiomeContainer)) { this.biomes = new ColumnBiomeContainer(levelIn.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), levelIn, levelIn); } + + if (levelIn.isClientSide) { + lightHeightmap = new ClientLightSurfaceTracker(this); + } else { + lightHeightmap = new LightSurfaceTrackerWrapper(this); + } + // TODO might want 4 columns that share the same BigCubes to have a reference to the same CubeMap? + cubeMap = new CubeMap(); + } + + @Inject( + method = "(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/ProtoChunk;Ljava/util/function/Consumer;)V", + at = @At("RETURN") + ) + private void onInitFromProtoChunk(ServerLevel serverLevel, ProtoChunk protoChunk, Consumer consumer, CallbackInfo ci) { + if (!this.isCubic()) { + return; + } + lightHeightmap = ((LightHeightmapGetter) protoChunk).getLightHeightmap(); + cubeMap = ((CubeMapGetter) protoChunk).getCubeMap(); + } + + @Inject( + method = "setBlockState(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Z)Lnet/minecraft/world/level/block/state/BlockState;", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/Heightmap;update(IIILnet/minecraft/world/level/block/state/BlockState;)Z", ordinal = 0) + ) + private void onSetBlock(BlockPos pos, BlockState state, boolean moved, CallbackInfoReturnable cir) { + if (!this.isCubic()) { + return; + } + // TODO client side light heightmap stuff + if (this.level.isClientSide) { + ClientLightSurfaceTracker clientLightHeightmap = ((LightHeightmapGetter) this).getClientLightHeightmap(); + } else { + int relX = pos.getX() & 15; + int relZ = pos.getZ() & 15; + LightSurfaceTrackerWrapper serverLightHeightmap = ((LightHeightmapGetter) this).getServerLightHeightmap(); + int oldHeight = serverLightHeightmap.getFirstAvailable(relX, relZ); + // Light heightmap update needs to occur before the light engine update. + // LevelChunk.setBlockState is called before the light engine is updated, so this works fine currently, but if this update call is ever moved, that must still be the case. + serverLightHeightmap.update(relX, pos.getY(), relZ, state); + int newHeight = serverLightHeightmap.getFirstAvailable(relX, relZ); + if (newHeight != oldHeight) { + ((ISkyLightColumnChecker) this.level.getChunkSource().getLightEngine()).checkSkyLightColumn(this, pos.getX(), pos.getZ(), oldHeight, newHeight); + } + } } @Redirect( diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkHolder.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkHolder.java index 6f633f00b..4f0b6b42d 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkHolder.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkHolder.java @@ -20,6 +20,7 @@ import io.github.opencubicchunks.cubicchunks.chunk.IChunkManager; import io.github.opencubicchunks.cubicchunks.chunk.ICubeHolder; import io.github.opencubicchunks.cubicchunks.chunk.ImposterChunkPos; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; import io.github.opencubicchunks.cubicchunks.chunk.cube.BigCube; import io.github.opencubicchunks.cubicchunks.chunk.cube.CubePrimer; import io.github.opencubicchunks.cubicchunks.chunk.cube.CubePrimerWrapper; @@ -78,7 +79,6 @@ public abstract class MixinChunkHolder implements ICubeHolder { @Shadow private CompletableFuture pendingFullStateConfirmation; @Shadow @Final private LevelHeightAccessor levelHeightAccessor; - private ChunkHolder[] chunkHolders = null; @Shadow private boolean hasChangedSections; private CubePos cubePos; // set from ASM @@ -311,39 +311,11 @@ private CompletableFuture scheduleChunkOrCube(ChunkMap chunkManager, ChunkHol } } - @Override - public void setChunkHolders(ChunkHolder[] chunkHolders) { - this.chunkHolders = chunkHolders; - } - - @Override - public ChunkHolder[] getChunkHolders() { - return chunkHolders; - } - // func_219276_a, getOrScheduleFuture - @Override public CompletableFuture> getOrScheduleCubeFuture(ChunkStatus targetStatus, ChunkMap chunkStorage) { - int i = targetStatus.getIndex(); - CompletableFuture> completableFuture = futures.get(i); - if (completableFuture != null) { - Either either = completableFuture.getNow(null); - if (either == null || either.left().isPresent()) { - return completableFuture; - } - } - - if (getStatus(this.ticketLevel).isOrAfter(targetStatus)) { - CompletableFuture> completableFuture2 = ((IChunkManager) chunkStorage).scheduleCube((ChunkHolder) (Object) this, - targetStatus); - this.updateChunkToSave(completableFuture2, "schedule " + targetStatus); - this.futures.set(i, completableFuture2); - return completableFuture2; - } else { - return completableFuture == null ? UNLOADED_CUBE_FUTURE : completableFuture; - } + @Override public CompletableFuture> getOrScheduleCubeFuture(ChunkStatus chunkStatus, ChunkMap chunkManager) { + return getOrScheduleFuture(chunkStatus, chunkManager); } - @Override public void addCubeStageListener(ChunkStatus status, BiConsumer, Throwable> consumer, ChunkMap chunkManager) { CompletableFuture> future = getOrScheduleFuture(status, chunkManager); @@ -385,6 +357,9 @@ public void blockChanged(BlockPos blockPos, CallbackInfo ci) { ci.cancel(); + int localX = blockPos.getX() & 0xF; + int localZ = blockPos.getZ() & 0xF; + if (cubePos == null) { ChunkAccess chunk = getTickingChunk(); if (chunk == null) { @@ -407,6 +382,18 @@ public void blockChanged(BlockPos blockPos, CallbackInfo ci) { changedLocalBlocks[sectionIDX].add(SectionPos.sectionRelativePos(blockPos)); } } + int topY = ((LightHeightmapGetter) chunk).getLightHeightmap().getFirstAvailable(localX, localZ) - 1; + // if the block being changed is new top block - heightmap probably was updated + // if block being changed is above new top block - heightmap was probably decreased + // TODO: replace heuristics with proper tracking + if (blockPos.getY() >= topY) { + // TODO: don't use heightmap type as "height" for address + + if (this.changedLocalBlocks[sectionIDX] == null) { + this.changedLocalBlocks[sectionIDX] = new ShortArraySet(); + } + changedLocalBlocks[sectionIDX].add(SectionPos.sectionRelativePos(blockPos)); + } return; } @@ -435,6 +422,7 @@ private void broadcastCubeChanges(LevelChunk levelChunk, CallbackInfo ci) { } ci.cancel(); + if (cubePos != null) { throw new IllegalStateException("Why is this getting called?"); } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkManager.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkManager.java index 40ce0a280..fb3a178f9 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkManager.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkManager.java @@ -5,10 +5,8 @@ import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; -import java.util.Arrays; import java.util.BitSet; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -16,6 +14,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BooleanSupplier; import java.util.function.IntFunction; @@ -57,6 +56,8 @@ import io.github.opencubicchunks.cubicchunks.chunk.util.Utils; import io.github.opencubicchunks.cubicchunks.mixin.access.common.EntityTrackerAccess; import io.github.opencubicchunks.cubicchunks.mixin.access.common.IOWorkerAccess; +import io.github.opencubicchunks.cubicchunks.mixin.access.common.TicketAccess; +import io.github.opencubicchunks.cubicchunks.mixin.access.common.TicketManagerAccess; import io.github.opencubicchunks.cubicchunks.network.PacketCubes; import io.github.opencubicchunks.cubicchunks.network.PacketDispatcher; import io.github.opencubicchunks.cubicchunks.network.PacketHeightmap; @@ -97,6 +98,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.server.level.Ticket; import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.util.Mth; import net.minecraft.util.thread.BlockableEventLoop; @@ -119,7 +121,6 @@ import net.minecraft.world.level.storage.DimensionDataStorage; import net.minecraft.world.level.storage.LevelStorageSource; import org.apache.commons.lang3.mutable.MutableBoolean; -import org.spongepowered.asm.mixin.Dynamic; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -136,6 +137,8 @@ public abstract class MixinChunkManager implements IChunkManager, IChunkMapInter private static final double TICK_UPDATE_DISTANCE = 128.0; + private static final Executor COLUMN_LOADING_EXECUTOR = Executors.newSingleThreadExecutor(); + @Shadow @Final ServerLevel level; @Shadow int viewDistance; @@ -316,7 +319,7 @@ protected void save(boolean flush, CallbackInfo ci) { } - public void setServerChunkCache(ServerChunkCache cache) { + @Override public void setServerChunkCache(ServerChunkCache cache) { serverChunkCache = cache; } @@ -468,7 +471,18 @@ private void scheduleCubeUnload(long cubePos, ChunkHolder chunkHolderIn) { ((IServerWorldLightManager) this.lightEngine).setCubeStatusEmpty(icube.getCubePos()); this.lightEngine.tryScheduleUpdate(); - ((ICubeStatusListener) this.progressListener).onCubeStatusChange(icube.getCubePos(), null); + CubePos pos = CubePos.from(cubePos); + + for (int localX = 0; localX < IBigCube.DIAMETER_IN_SECTIONS; localX++) { + for (int localZ = 0; localZ < IBigCube.DIAMETER_IN_SECTIONS; localZ++) { + long chunkPos = pos.asChunkPos(localX, localZ).toLong(); + Ticket[] tickets = ((TicketManagerAccess) distanceManager).invokeGetTickets(chunkPos).stream().filter((ticket -> + ticket.getType() == CCTicketType.CCCOLUMN && ((TicketAccess) ticket).getKey().equals(pos))).toArray(Ticket[]::new); + for (Ticket ticket : tickets) { + ((TicketManagerAccess) this.distanceManager).invokeRemoveTicket(chunkPos, ticket); + } + } + } } } @@ -580,55 +594,24 @@ private void onPromoteChunkMap(CallbackInfoReturnable cir) { this.visibleCubeMap = updatingCubeMap.clone(); } - @Dynamic - @Inject(method = "updateCubeScheduling", at = @At(value = "INVOKE", target = "Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;put(JLjava/lang/Object;)Ljava/lang/Object;")) - private void addChunkHolders(long pos, int _level, ChunkHolder holder, int i, CallbackInfoReturnable cir) { - ICubeHolder cubeHolder = (ICubeHolder) holder; - if (cubeHolder.getChunkHolders() != null) { - return; - } - CubePos cubePos = CubePos.from(pos); - ChunkHolder[] chunkHolders = new ChunkHolder[IBigCube.DIAMETER_IN_SECTIONS * IBigCube.DIAMETER_IN_SECTIONS]; - for (int localX = 0; localX < IBigCube.DIAMETER_IN_SECTIONS; localX++) { - for (int localZ = 0; localZ < IBigCube.DIAMETER_IN_SECTIONS; localZ++) { - ChunkPos chunkPos = cubePos.asChunkPos(localX, localZ); - ChunkHolder chunkHolder = ((IServerChunkProvider) serverChunkCache).getChunkHolderForce(chunkPos, ChunkStatus.EMPTY); - chunkHolders[localX * IBigCube.DIAMETER_IN_SECTIONS + localZ] = chunkHolder; - } - } - cubeHolder.setChunkHolders(chunkHolders); - } - @Override public Iterable getCubes() { return Iterables.unmodifiableIterable(this.visibleCubeMap.values()); } - //weird supplier to make sure the cube future is created after the chunk ones, just in case - private CompletableFuture> chainFutures( - Supplier>> cubeFutureSupplier, ChunkHolder[] chunkHolders, ChunkStatus chunkStatusIn) { - Iterator iterator = Arrays.stream(chunkHolders).iterator(); - ChunkHolder chunkHolder = iterator.next(); //set first future - CompletableFuture> chunkFutureChain = chunkHolder.getOrScheduleFuture(chunkStatusIn, (ChunkMap) (Object) this); - while (iterator.hasNext()) { //chain all subsequent futures - ChunkHolder next = iterator.next(); - chunkFutureChain = chunkFutureChain.thenComposeAsync((either) -> next.getOrScheduleFuture(chunkStatusIn, (ChunkMap) (Object) this)); - } - - CompletableFuture> cubeFuture = cubeFutureSupplier.get(); - return chunkFutureChain.thenComposeAsync((either) -> cubeFuture); - } - // func_219244_a, schedule @Override public CompletableFuture> scheduleCube(ChunkHolder cubeHolder, ChunkStatus chunkStatusIn) { CubePos cubePos = ((ICubeHolder) cubeHolder).getCubePos(); + CompletableFuture> columnFutures = scheduleAndWaitForColumns(chunkStatusIn, cubePos); if (chunkStatusIn == ChunkStatus.EMPTY) { - return chainFutures(() -> scheduleCubeLoad(cubePos), ((ICubeHolder) cubeHolder).getChunkHolders(), chunkStatusIn); + CompletableFuture> cubeFuture = this.scheduleCubeLoad(cubePos); + return columnFutures.thenComposeAsync(columns -> cubeFuture); } else { - CompletableFuture> completablefuture = chainFutures( - () -> ((ICubeHolder) cubeHolder).getOrScheduleCubeFuture(chunkStatusIn.getParent(), (ChunkMap) (Object) this), ((ICubeHolder) cubeHolder).getChunkHolders(), chunkStatusIn); - return completablefuture.thenComposeAsync( + CompletableFuture> parentCubeFuture = Utils.unsafeCast( + ((ICubeHolder) cubeHolder).getOrScheduleCubeFuture(chunkStatusIn.getParent(), (ChunkMap) (Object) this) + ); + return columnFutures.thenComposeAsync(columns -> parentCubeFuture).thenComposeAsync( (Either inputSection) -> { Optional optional = inputSection.left(); if (!optional.isPresent()) { @@ -669,12 +652,8 @@ private ChunkStatus getCubeDependencyStatus(ChunkStatus status, int distance) { private CompletableFuture> scheduleCubeGeneration(ChunkHolder chunkHolderIn, ChunkStatus chunkStatusIn) { CubePos cubePos = ((ICubeHolder) chunkHolderIn).getCubePos(); CompletableFuture, ChunkHolder.ChunkLoadingFailure>> future = - this.getCubeRangeFuture(cubePos, CubeStatus.getCubeTaskRange(chunkStatusIn), (count) -> { - return this.getCubeDependencyStatus(chunkStatusIn, count); - }); - this.level.getProfiler().incrementCounter(() -> { - return "cubeGenerate " + chunkStatusIn.getName(); - }); + this.getCubeRangeFuture(cubePos, CubeStatus.getCubeTaskRange(chunkStatusIn), (count) -> this.getCubeDependencyStatus(chunkStatusIn, count)); + this.level.getProfiler().incrementCounter(() -> "cubeGenerate " + chunkStatusIn.getName()); Executor executor = (runnable) -> this.cubeWorldgenMailbox.tell(CubeTaskPriorityQueueSorter.createMsg(chunkHolderIn, runnable)); @@ -682,9 +661,8 @@ private CompletableFuture> sch return sectionOrError.map((neighborSections) -> { try { CompletableFuture> finalFuture = Utils.unsafeCast( - chunkStatusIn.generate(executor, this.level, this.generator, this.structureManager, this.lightEngine, (chunk) -> { - return Utils.unsafeCast(this.protoCubeToFullCube(chunkHolderIn)); - }, Utils.unsafeCast(neighborSections))); + chunkStatusIn.generate(executor, this.level, this.generator, this.structureManager, this.lightEngine, + (chunk) -> Utils.unsafeCast(this.protoCubeToFullCube(chunkHolderIn)), Utils.unsafeCast(neighborSections))); ((ICubeStatusListener) this.progressListener).onCubeStatusChange(cubePos, chunkStatusIn); return finalFuture; } catch (Exception exception) { @@ -695,13 +673,32 @@ private CompletableFuture> sch crashreportcategory.setDetail("Generator", this.generator); throw new ReportedException(crashreport); } - }, (p_219211_2_) -> { + }, (loadingFailure) -> { this.releaseLightTicket(cubePos); - return CompletableFuture.completedFuture(Either.right(p_219211_2_)); + return CompletableFuture.completedFuture(Either.right(loadingFailure)); }); - }, (runnable) -> { - this.cubeWorldgenMailbox.tell(CubeTaskPriorityQueueSorter.createMsg(chunkHolderIn, runnable)); - }); + }, executor); + } + + private CompletableFuture> scheduleAndWaitForColumns(ChunkStatus chunkStatusIn, CubePos cubePos) { + + CompletableFuture>> nestedChainedFuture = CompletableFuture.supplyAsync(() -> { + CompletableFuture> chainedFutures = null; + for (int localX = 0; localX < IBigCube.DIAMETER_IN_SECTIONS; localX++) { + for (int localZ = 0; localZ < IBigCube.DIAMETER_IN_SECTIONS; localZ++) { + ChunkPos chunkPos = cubePos.asChunkPos(localX, localZ); + CompletableFuture> columnFutureForCube = + ((IServerChunkProvider) serverChunkCache).getColumnFutureForCube(cubePos, chunkPos.x, chunkPos.z, chunkStatusIn, true); + if (chainedFutures == null) { + chainedFutures = columnFutureForCube; + } else { + chainedFutures = chainedFutures.thenCompose((existingFuture) -> columnFutureForCube); + } + } + } + return chainedFutures; + }, mainThreadExecutor); + return nestedChainedFuture.thenComposeAsync((future) -> future, COLUMN_LOADING_EXECUTOR); } // func_219236_a, getChunkRangeFuture diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkStatus.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkStatus.java index be8cb8fb6..0816a0f1a 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkStatus.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinChunkStatus.java @@ -19,7 +19,9 @@ import io.github.opencubicchunks.cubicchunks.config.ChunkGeneratorSettings; import io.github.opencubicchunks.cubicchunks.config.reloadlisteners.ChunkGeneratorSettingsReloadListener; import io.github.opencubicchunks.cubicchunks.mixin.access.common.ChunkGeneratorAccess; +import io.github.opencubicchunks.cubicchunks.mixin.access.common.ChunkManagerAccess; import io.github.opencubicchunks.cubicchunks.mixin.access.common.StructureFeatureManagerAccess; +import io.github.opencubicchunks.cubicchunks.mixin.access.common.ThreadedLevelLightEngineAccess; import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; import io.github.opencubicchunks.cubicchunks.utils.Coords; import io.github.opencubicchunks.cubicchunks.world.CubeWorldGenRegion; @@ -462,6 +464,10 @@ private static void lightChunkCC(ChunkStatus status, ThreadedLevelLightEngine li } if (!(chunk instanceof CubePrimer)) { + ChunkPos pos = chunk.getPos(); + ((ThreadedLevelLightEngineAccess) lightManager).invokeAddTask(pos.x, pos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, () -> { + ((ChunkManagerAccess) ((ThreadedLevelLightEngineAccess) lightManager).getChunkMap()).invokeReleaseLightTicket(pos); + }); if (!chunk.getStatus().isOrAfter(status)) { ((ProtoChunk) chunk).setStatus(status); } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinImposterProtoChunk.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinImposterProtoChunk.java new file mode 100644 index 000000000..57ffcf388 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinImposterProtoChunk.java @@ -0,0 +1,26 @@ +package io.github.opencubicchunks.cubicchunks.mixin.core.common.chunk; + +import io.github.opencubicchunks.cubicchunks.chunk.CubeMap; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; +import net.minecraft.world.level.chunk.ImposterProtoChunk; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.levelgen.Heightmap; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ImposterProtoChunk.class) +public class MixinImposterProtoChunk implements CubeMapGetter, LightHeightmapGetter { + + + @Shadow @Final private LevelChunk wrapped; + + @Override public CubeMap getCubeMap() { + return ((CubeMapGetter) this.wrapped).getCubeMap(); + } + + @Override public Heightmap getLightHeightmap() { + return ((LightHeightmapGetter) this.wrapped).getLightHeightmap(); + } +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinProtoChunk.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinProtoChunk.java index 768411f24..9b98cc719 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinProtoChunk.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/chunk/MixinProtoChunk.java @@ -1,18 +1,24 @@ package io.github.opencubicchunks.cubicchunks.mixin.core.common.chunk; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMap; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; import io.github.opencubicchunks.cubicchunks.chunk.cube.CubePrimer; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.LightSurfaceTrackerWrapper; import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; import net.minecraft.core.SectionPos; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.ProtoTickList; import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.material.Fluid; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -24,16 +30,36 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(ProtoChunk.class) -public abstract class MixinProtoChunk implements LevelHeightAccessor, CubicLevelHeightAccessor { +public abstract class MixinProtoChunk implements LightHeightmapGetter, LevelHeightAccessor, CubeMapGetter, CubicLevelHeightAccessor { private boolean isCubic; private boolean generates2DChunks; private WorldStyle worldStyle; + private LightSurfaceTrackerWrapper lightHeightmap; + private CubeMap cubeMap; + @Shadow @Final private LevelHeightAccessor levelHeightAccessor; @Shadow public abstract ChunkStatus getStatus(); + @Override + public Heightmap getLightHeightmap() { + if (!isCubic) { + throw new UnsupportedOperationException("Attempted to get light heightmap on a non-cubic chunk"); + } + return lightHeightmap; + } + + @Override + public CubeMap getCubeMap() { + // TODO actually init this properly instead of doing lazy init here + if (cubeMap == null) { + cubeMap = new CubeMap(); + } + return cubeMap; + } + @Inject(method = "(Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/world/level/chunk/UpgradeData;[Lnet/minecraft/world/level/chunk/LevelChunkSection;" + "Lnet/minecraft/world/level/chunk/ProtoTickList;Lnet/minecraft/world/level/chunk/ProtoTickList;Lnet/minecraft/world/level/LevelHeightAccessor;)V", at = @At("RETURN")) private void setCubic(ChunkPos chunkPos, UpgradeData upgradeData, LevelChunkSection[] levelChunkSections, ProtoTickList protoTickList, ProtoTickList protoTickList2, @@ -104,4 +130,18 @@ private void setMinHeight(CallbackInfoReturnable cir) { @Override public boolean generates2DChunks() { return generates2DChunks; } + + @Inject( + method = "setStatus(Lnet/minecraft/world/level/chunk/ChunkStatus;)V", + at = @At("RETURN") + ) + private void onSetStatus(ChunkStatus status, CallbackInfo ci) { + if (!this.isCubic()) { + return; + } + if (lightHeightmap == null && this.getStatus().isOrAfter(ChunkStatus.FEATURES)) { + // Lighting only starts happening after FEATURES, so we init here to avoid creating unnecessary heightmaps + lightHeightmap = new LightSurfaceTrackerWrapper((ChunkAccess) this); + } + } } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/entity/MixinServerPlayerEntity.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/entity/MixinServerPlayerEntity.java index 0a4a37f34..e12d4b507 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/entity/MixinServerPlayerEntity.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/entity/MixinServerPlayerEntity.java @@ -1,17 +1,25 @@ package io.github.opencubicchunks.cubicchunks.mixin.core.common.entity; +import com.mojang.authlib.GameProfile; import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; +import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.Packet; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; @Mixin(ServerPlayer.class) -public abstract class MixinServerPlayerEntity { +public abstract class MixinServerPlayerEntity extends Player { + public MixinServerPlayerEntity(Level level, BlockPos blockPos, float f, GameProfile gameProfile) { + super(level, blockPos, f, gameProfile); + } + @Shadow public abstract ServerLevel getLevel(); @Redirect(method = "trackChunk", @@ -21,4 +29,13 @@ public void onSendChunkLoad(ServerGamePacketListenerImpl serverPlayNetHandler, P serverPlayNetHandler.send(packetIn); } } + + // This debug code probably causes considerable lag and other issues; it should only be used while debugging lighting +// @Inject(method = "tick", +// at = @At("HEAD")) +// private void onTick(CallbackInfo ci) { +// if (((CubicLevelHeightAccessor) this.getLevel()).isCubic()) { +// PacketDispatcher.sendTo(new PacketUpdateLight(CubePos.from(new BlockPos(this.position())), this.level.getLightEngine(), true), (ServerPlayer) (Object) this); +// } +// } } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/ticket/MixinTicketManager.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/ticket/MixinTicketManager.java index 690c9b765..27b9d0e85 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/ticket/MixinTicketManager.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/ticket/MixinTicketManager.java @@ -20,7 +20,6 @@ import io.github.opencubicchunks.cubicchunks.chunk.ticket.PlayerCubeTracker; import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; import io.github.opencubicchunks.cubicchunks.mixin.access.common.ChunkHolderAccess; -import io.github.opencubicchunks.cubicchunks.mixin.access.common.ChunkManagerAccess; import io.github.opencubicchunks.cubicchunks.mixin.access.common.TicketAccess; import io.github.opencubicchunks.cubicchunks.utils.Coords; import io.github.opencubicchunks.cubicchunks.utils.MathUtil; @@ -34,14 +33,13 @@ import it.unimi.dsi.fastutil.objects.ObjectSet; import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ChunkTaskPriorityQueueSorter; import net.minecraft.server.level.DistanceManager; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.Ticket; import net.minecraft.server.level.TicketType; import net.minecraft.util.SortedArraySet; import net.minecraft.util.thread.ProcessorHandle; -import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.ChunkPos; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -53,6 +51,9 @@ @Mixin(DistanceManager.class) public abstract class MixinTicketManager implements ITicketManager, IVerticalView { + @Final @Shadow private Executor mainThreadExecutor; + @Shadow private long ticketTickCounter; + private final Long2ObjectOpenHashMap>> cubeTickets = new Long2ObjectOpenHashMap<>(); private final Long2ObjectMap> playersPerCube = new Long2ObjectOpenHashMap<>(); @@ -68,27 +69,11 @@ public abstract class MixinTicketManager implements ITicketManager, IVerticalVie private boolean isCubic; - @Shadow @Final private DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter; - - @Shadow @Final private DistanceManager.PlayerTicketTracker playerTicketManager; - - @Shadow @Final private DistanceManager.ChunkTicketTracker ticketTracker; - - @Shadow @Final private Set chunksToUpdateFutures; - - @Shadow @Final private LongSet ticketsToRelease; - - @Shadow @Final private ProcessorHandle ticketThrottlerReleaser; - - @Final @Shadow private Executor mainThreadExecutor; - - @Shadow private long ticketTickCounter; - @Shadow private static int getTicketLevelAt(SortedArraySet> p_229844_0_) { throw new Error("Mixin did not apply correctly"); } - @Shadow protected abstract SortedArraySet> getTickets(long position); + @Shadow public abstract void addTicket(TicketType type, ChunkPos pos, int level, T argument); @Inject(method = "", at = @At("RETURN")) public void init(Executor executor, Executor executor2, CallbackInfo ci) { @@ -103,6 +88,11 @@ public void init(Executor executor, Executor executor2, CallbackInfo ci) { public void addCubeTicket(long cubePosIn, Ticket ticketIn) { SortedArraySet> sortedarrayset = this.getCubeTickets(cubePosIn); int i = getTicketLevelAt(sortedarrayset); + + // force a ticket on the cube's column + CubePos cubePos = CubePos.from(cubePosIn); + addTicket(CCTicketType.CCCOLUMN, cubePos.asChunkPos(), i, cubePos); + Ticket ticket = sortedarrayset.addOrGet(ticketIn); ((TicketAccess) ticket).invokeSetCreatedTick(this.ticketTickCounter); if (ticketIn.getTicketLevel() < i) { @@ -129,45 +119,6 @@ private SortedArraySet> getCubeTickets(long cubePos) { //BEGIN INJECT - @Override public boolean runAllUpdatesForChunks(ChunkMap chunkMap) { - this.naturalSpawnChunkCounter.runAllUpdates(); - this.playerTicketManager.runAllUpdates(); - int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE); - boolean bl = i != 0; - - if (!this.chunksToUpdateFutures.isEmpty()) { - this.chunksToUpdateFutures.forEach((chunkHolder) -> ((ChunkHolderAccess) chunkHolder).invokeUpdateFutures(chunkMap, this.mainThreadExecutor)); - this.chunksToUpdateFutures.clear(); - return true; - } else { - if (!this.ticketsToRelease.isEmpty()) { - LongIterator longIterator = this.ticketsToRelease.iterator(); - - while (longIterator.hasNext()) { - long l = longIterator.nextLong(); - if (this.getTickets(l).stream().anyMatch((ticket) -> ticket.getType() == TicketType.PLAYER)) { - ChunkHolder chunkHolder = ((ChunkManagerAccess) chunkMap).invokeGetUpdatingChunkIfPresent(l); - if (chunkHolder == null) { - throw new IllegalStateException(); - } - - CompletableFuture> completableFuture = chunkHolder.getEntityTickingChunkFuture(); - completableFuture.thenAccept((either) -> { - this.mainThreadExecutor.execute(() -> { - this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> { - }, l, false)); - }); - }); - } - } - - this.ticketsToRelease.clear(); - } - - return bl; - } - } - @Inject(method = "runAllUpdates", at = @At("RETURN"), cancellable = true) public void processUpdates(ChunkMap chunkManager, CallbackInfoReturnable callbackInfoReturnable) { if (!isCubic) { diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinBlockLightEngine.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinBlockLightEngine.java index 1498785dd..4b5a28cf2 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinBlockLightEngine.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinBlockLightEngine.java @@ -13,7 +13,7 @@ @SuppressWarnings("rawtypes") @Mixin(BlockLightEngine.class) -public abstract class MixinBlockLightEngine extends MixinLightEngine { +public abstract class MixinBlockLightEngine extends MixinLayerLightEngine { /** * @author NotStirred diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinDynamicGraphMinFixedPoint.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinDynamicGraphMinFixedPoint.java new file mode 100644 index 000000000..adf46e6a1 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinDynamicGraphMinFixedPoint.java @@ -0,0 +1,13 @@ +package io.github.opencubicchunks.cubicchunks.mixin.core.common.world.lighting; + +import net.minecraft.world.level.lighting.DynamicGraphMinFixedPoint; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(DynamicGraphMinFixedPoint.class) +public abstract class MixinDynamicGraphMinFixedPoint { + @Shadow protected abstract void checkNeighbor(long sourceId, long targetId, int level, boolean decrease); + + @Shadow + protected abstract void checkEdge(long sourceId, long id, int level, boolean decrease); +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLightEngine.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLayerLightEngine.java similarity index 61% rename from src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLightEngine.java rename to src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLayerLightEngine.java index fd26b1e62..aec484fc5 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLightEngine.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLayerLightEngine.java @@ -4,7 +4,7 @@ import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; -import io.github.opencubicchunks.cubicchunks.mixin.access.common.SectionLightStorageAccess; +import io.github.opencubicchunks.cubicchunks.mixin.access.common.LayerLightSectionStorageAccess; import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; import io.github.opencubicchunks.cubicchunks.world.lighting.ICubeLightProvider; import io.github.opencubicchunks.cubicchunks.world.lighting.ILightEngine; @@ -14,36 +14,36 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LightChunkGetter; import net.minecraft.world.level.lighting.DataLayerStorageMap; import net.minecraft.world.level.lighting.LayerLightEngine; import net.minecraft.world.level.lighting.LayerLightSectionStorage; -import org.apache.commons.lang3.mutable.MutableInt; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(LayerLightEngine.class) -public abstract class MixinLightEngine, S extends LayerLightSectionStorage> implements ILightEngine { +public abstract class MixinLayerLightEngine, S extends LayerLightSectionStorage> extends MixinDynamicGraphMinFixedPoint implements ILightEngine { + @Shadow @Final protected S storage; @Shadow @Final protected BlockPos.MutableBlockPos pos; @Shadow @Final protected LightChunkGetter chunkSource; + protected boolean isCubic; + @Shadow @Final private long[] lastChunkPos; @Shadow @Final private BlockGetter[] lastChunk; - private boolean isCubic; - private boolean generates2DChunks; - private CubicLevelHeightAccessor.WorldStyle worldStyle; + @Shadow protected void checkNode(long id) { + } @Shadow @Nullable protected abstract BlockGetter getChunk(int chunkX, int chunkZ); @@ -59,7 +59,7 @@ public void enableLightSources(CubePos cubePos, boolean enable) { //TODO: implement invokeEnableLightSources for CubePos in SkyLightStorage for (int x = 0; x < IBigCube.DIAMETER_IN_SECTIONS; x++) { for (int z = 0; z < IBigCube.DIAMETER_IN_SECTIONS; z++) { - ((SectionLightStorageAccess) this.storage).invokeSetColumnEnabled(new ChunkPos(chunkPos.x + x, chunkPos.z + z).toLong(), enable); + ((LayerLightSectionStorageAccess) this.storage).invokeSetColumnEnabled(new ChunkPos(chunkPos.x + x, chunkPos.z + z).toLong(), enable); } } } @@ -67,50 +67,20 @@ public void enableLightSources(CubePos cubePos, boolean enable) { @Inject(method = "", at = @At("RETURN")) private void setCubic(LightChunkGetter lightChunkGetter, LightLayer lightLayer, S layerLightSectionStorage, CallbackInfo ci) { this.isCubic = ((CubicLevelHeightAccessor) this.chunkSource.getLevel()).isCubic(); - this.generates2DChunks = ((CubicLevelHeightAccessor) this.chunkSource.getLevel()).generates2DChunks(); - this.worldStyle = ((CubicLevelHeightAccessor) this.chunkSource.getLevel()).worldStyle(); +// this.generates2DChunks = ((CubicLevelHeightAccessor) this.chunkSource.getLevel()).generates2DChunks(); +// this.worldStyle = ((CubicLevelHeightAccessor) this.chunkSource.getLevel()).worldStyle(); } - /** - * @author NotStirred - * @reason Vanilla lighting is gone - */ - //TODO: make this into a redirect that calls getCubeReader taking arguments blockPosLong - @Inject(method = "getStateAndOpacity", at = @At("HEAD"), cancellable = true) - private void getStateAndOpacity(long blockPosLong, @Nullable MutableInt opacity, CallbackInfoReturnable cir) { + @Redirect(method = "getStateAndOpacity", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/lighting/LayerLightEngine;getChunk(II)Lnet/minecraft/world/level/BlockGetter;")) + private BlockGetter useCubicBlockGetter(LayerLightEngine layerLightEngine, int chunkX, int chunkZ, long blockPos) { if (!this.isCubic) { - return; - } - cir.cancel(); - - if (blockPosLong == Long.MAX_VALUE) { - if (opacity != null) { - opacity.setValue(0); - } - - cir.setReturnValue(Blocks.AIR.defaultBlockState()); - } else { - int sectionX = SectionPos.blockToSectionCoord(BlockPos.getX(blockPosLong)); - int sectionY = SectionPos.blockToSectionCoord(BlockPos.getY(blockPosLong)); - int sectionZ = SectionPos.blockToSectionCoord(BlockPos.getZ(blockPosLong)); - BlockGetter iblockreader = this.getCubeReader(sectionX, sectionY, sectionZ); - if (iblockreader == null) { - if (opacity != null) { - opacity.setValue(16); - } - - cir.setReturnValue(Blocks.BEDROCK.defaultBlockState()); - } else { - this.pos.set(blockPosLong); - BlockState blockstate = iblockreader.getBlockState(this.pos); - boolean flag = blockstate.canOcclude() && blockstate.useShapeForLightOcclusion(); - if (opacity != null) { - opacity.setValue(blockstate.getLightBlock(this.chunkSource.getLevel(), this.pos)); - } - - cir.setReturnValue(flag ? blockstate : Blocks.AIR.defaultBlockState()); - } + return this.getChunk(chunkX, chunkZ); } + int sectionX = SectionPos.blockToSectionCoord(BlockPos.getX(blockPos)); + int sectionY = SectionPos.blockToSectionCoord(BlockPos.getY(blockPos)); + int sectionZ = SectionPos.blockToSectionCoord(BlockPos.getZ(blockPos)); + return this.getCubeReader(sectionX, sectionY, sectionZ); } @Nullable diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinSectionLightStorage.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLayerLightSectionStorage.java similarity index 90% rename from src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinSectionLightStorage.java rename to src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLayerLightSectionStorage.java index 01e273d09..82ff573f5 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinSectionLightStorage.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLayerLightSectionStorage.java @@ -1,6 +1,5 @@ package io.github.opencubicchunks.cubicchunks.mixin.core.common.world.lighting; -import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; import io.github.opencubicchunks.cubicchunks.mixin.access.common.LevelBasedGraphAccess; import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; import io.github.opencubicchunks.cubicchunks.world.lighting.ISectionLightStorage; @@ -25,7 +24,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(LayerLightSectionStorage.class) -public abstract class MixinSectionLightStorage> extends SectionTracker implements ISectionLightStorage { +public abstract class MixinLayerLightSectionStorage> extends SectionTracker implements ISectionLightStorage { @Shadow @Final private static Direction[] DIRECTIONS; @@ -38,7 +37,7 @@ public abstract class MixinSectionLightStorage> private final LongSet cubesToRetain = new LongOpenHashSet(); - protected MixinSectionLightStorage(int p_i50706_1_, int p_i50706_2_, int p_i50706_3_) { + protected MixinLayerLightSectionStorage(int p_i50706_1_, int p_i50706_2_, int p_i50706_3_) { super(p_i50706_1_, p_i50706_2_, p_i50706_3_); } @@ -78,13 +77,15 @@ protected void markNewInconsistenciesForCube(LayerLightEngine engine, bool this.clearQueuedSectionBlocks(engine, noLightPos); DataLayer nibblearray = this.queuedSections.remove(noLightPos); DataLayer nibblearray1 = this.updatingSectionData.removeLayer(noLightPos); - if (this.cubesToRetain.contains(CubePos.sectionToCubeSectionLong(noLightPos))) { + // FIXME this commented out check is probably important, but also breaks client-side lighting + // should investigate why it breaks things instead of just disabling it. +// if (this.cubesToRetain.contains(CubePos.sectionToCubeSectionLong(noLightPos))) { if (nibblearray != null) { this.queuedSections.put(noLightPos, nibblearray); } else if (nibblearray1 != null) { this.queuedSections.put(noLightPos, nibblearray1); } - } +// } } this.updatingSectionData.clearCache(); @@ -99,14 +100,16 @@ protected void markNewInconsistenciesForCube(LayerLightEngine engine, bool for (Long2ObjectMap.Entry entry : this.queuedSections.long2ObjectEntrySet()) { long entryPos = entry.getLongKey(); - if (this.storingLightForSection(entryPos)) { + // FIXME this commented out check is also probably important, but this one breaks *server-side* lighting + // should investigate why it breaks things instead of just disabling it. +// if (this.storingLightForSection(entryPos)) { DataLayer nibblearray2 = entry.getValue(); if (this.updatingSectionData.getLayer(entryPos) != nibblearray2) { this.clearQueuedSectionBlocks(engine, entryPos); this.updatingSectionData.setLayer(entryPos, nibblearray2); this.changedSections.add(entryPos); } - } +// } } LevelBasedGraphAccess engineAccess = ((LevelBasedGraphAccess) engine); diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinWorldLightManager.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLevelLightEngine.java similarity index 72% rename from src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinWorldLightManager.java rename to src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLevelLightEngine.java index 533733d0f..29b84249a 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinWorldLightManager.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinLevelLightEngine.java @@ -2,8 +2,12 @@ import javax.annotation.Nullable; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; +import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; +import io.github.opencubicchunks.cubicchunks.world.lighting.ICubicSkyLightEngine; import io.github.opencubicchunks.cubicchunks.world.lighting.ILightEngine; +import io.github.opencubicchunks.cubicchunks.world.lighting.ISkyLightColumnChecker; import io.github.opencubicchunks.cubicchunks.world.lighting.IWorldLightManager; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; @@ -18,7 +22,7 @@ import org.spongepowered.asm.mixin.Shadow; @Mixin(LevelLightEngine.class) -public abstract class MixinWorldLightManager implements IWorldLightManager, LightEventListener { +public abstract class MixinLevelLightEngine implements IWorldLightManager, LightEventListener, ISkyLightColumnChecker { @Shadow @Final protected LevelHeightAccessor levelHeightAccessor; @Shadow @Final @Nullable private LayerLightEngine blockEngine; @@ -62,5 +66,16 @@ public void enableLightSources(CubePos cubePos, boolean retain) { } } + protected void doSkyLightForCube(IBigCube cube) { + if (this.skyEngine != null) { + ((ICubicSkyLightEngine) this.skyEngine).doSkyLightForCube(cube); + } + } + @Override + public void checkSkyLightColumn(CubeMapGetter chunk, int x, int z, int oldHeight, int newHeight) { + if (this.skyEngine != null) { + ((ISkyLightColumnChecker) skyEngine).checkSkyLightColumn(chunk, x, z, oldHeight, newHeight); + } + } } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinSkyLightEngine.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinSkyLightEngine.java new file mode 100644 index 000000000..672f34460 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinSkyLightEngine.java @@ -0,0 +1,223 @@ +package io.github.opencubicchunks.cubicchunks.mixin.core.common.world.lighting; + + +import io.github.opencubicchunks.cubicchunks.chunk.CubeMap; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; +import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; +import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; +import io.github.opencubicchunks.cubicchunks.mixin.access.common.LayerLightSectionStorageAccess; +import io.github.opencubicchunks.cubicchunks.utils.Coords; +import io.github.opencubicchunks.cubicchunks.world.lighting.ICubeLightProvider; +import io.github.opencubicchunks.cubicchunks.world.lighting.ICubicSkyLightEngine; +import io.github.opencubicchunks.cubicchunks.world.lighting.ISkyLightColumnChecker; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.lighting.SkyLightEngine; +import net.minecraft.world.level.lighting.SkyLightSectionStorage; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(SkyLightEngine.class) +public abstract class MixinSkyLightEngine extends MixinLayerLightEngine implements ISkyLightColumnChecker, + ICubicSkyLightEngine { + @Shadow @Final private static Direction[] DIRECTIONS; + + /** + * @author CursedFlames + * @reason disable vanilla sky light logic + */ + @Inject(method = "checkNode", at = @At("HEAD"), cancellable = true) + protected void checkNode(long id, CallbackInfo ci) { + if (!this.isCubic) { + return; + } + ci.cancel(); + super.checkNode(id); + } + + /** all parameters are global coordinates */ + @Override public void checkSkyLightColumn(CubeMapGetter chunk, int x, int z, int oldHeight, int newHeight) { + ((LayerLightSectionStorageAccess) this.storage).invokeRunAllUpdates(); + CubeMap cubeMap = chunk.getCubeMap(); + int oldHeightCube = Coords.blockToCube(oldHeight - 1); + int newHeightCube = Coords.blockToCube(newHeight); + if (oldHeight > newHeight) { + // not sure if this is necessary - also maybe it should be done inside the loop? not sure if threaded stuff can result in storage becoming out of date inside the loop + ((LayerLightSectionStorageAccess) this.storage).invokeRunAllUpdates(); + + // TODO cube iteration order might still be important here + // (int y = oldHeight-1; y >= newHeight; y--) + for (int cubeY : cubeMap.getLoaded()) { + if (oldHeightCube <= cubeY && cubeY <= newHeightCube) { + for (int dy = IBigCube.DIAMETER_IN_BLOCKS - 1; dy >= 0; dy--) { + int y = cubeY * IBigCube.DIAMETER_IN_BLOCKS + dy; + + if (y >= oldHeight) { + continue; + } + if (y < newHeight) { + break; + } + + long pos = new BlockPos(x, y, z).asLong(); + if (((LayerLightSectionStorageAccess) this.storage).invokeStoringLightForSection(SectionPos.blockToSection(pos))) { + addEmissionAtPos(pos); + } + } + } + } + } else { + // TODO cube iteration order might still be important here + // (int y = oldHeight; y < newHeight; y++) + for (int cubeY : cubeMap.getLoaded()) { + if (oldHeightCube <= cubeY && cubeY <= newHeightCube) { + for (int dy = 0; dy < IBigCube.DIAMETER_IN_BLOCKS; dy++) { + int y = cubeY * IBigCube.DIAMETER_IN_BLOCKS + dy; + + if (y < oldHeight) { + continue; + } + if (y >= newHeight) { + break; + } + + long pos = new BlockPos(x, y, z).asLong(); + // Don't need to check storing light for pos here, since it's already handled by checkNode + this.checkNode(pos); + } + } + } + } + } + + private void addEmissionAtPos(long pos) { + this.checkEdge(Long.MAX_VALUE, pos, 0, true); + } + + /** + * @author CursedFlames + * @reason Prevent getComputedLevel from ignoring skylight sources and decreasing light level - similar to BlockLightEngine's light source check + */ + @Inject(method = "getComputedLevel(JJI)I", + at = @At("HEAD"), cancellable = true) + private void onGetComputedLevel(long id, long excludedId, int maxLevel, CallbackInfoReturnable cir) { + // TODO do we want this mixin on client side too? + if (!this.isCubic /*|| this.chunkSource.getLevel() instanceof ClientLevel*/) { + return; + } + BlockPos pos = BlockPos.of(id); + + BlockGetter cube = ((ICubeLightProvider) this.chunkSource).getCubeForLighting( + Coords.blockToSection(pos.getX()), Coords.blockToSection(pos.getY()), Coords.blockToSection(pos.getZ())); + if (cube == null || !((IBigCube) cube).getStatus().isOrAfter(ChunkStatus.LIGHT)) { + return; + } + + BlockGetter chunk = this.chunkSource.getChunkForLighting(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())); + + if (chunk == null) { + // ToDo Known bug: Cubes may be sent to the client ahead of their chunks. This is not fatal. + // see MixinSkyLightSectionStorage.onGetLightValue(...) +// System.out.println("getComputedLevel: Missing chunk for lighting."); + return; + } + + Heightmap heightmap = ((LightHeightmapGetter) chunk).getLightHeightmap(); + + int height = heightmap.getFirstAvailable(pos.getX() & 0xF, pos.getZ() & 0xF); + if (height <= pos.getY()) { + cir.setReturnValue(0); + } + } + + @Override + public void doSkyLightForCube(IBigCube cube) { + CubePos cubePos = cube.getCubePos(); + ChunkPos chunkPos = cubePos.asChunkPos(); + int minY = cubePos.minCubeY(); + int maxY = cubePos.maxCubeY(); + for (int sectionX = 0; sectionX < IBigCube.DIAMETER_IN_SECTIONS; sectionX++) { + for (int sectionZ = 0; sectionZ < IBigCube.DIAMETER_IN_SECTIONS; sectionZ++) { + + BlockGetter chunk = this.chunkSource.getChunkForLighting(chunkPos.x + sectionX, chunkPos.z + sectionZ); + + // the load order guarantees the chunk being present + assert (chunk != null); + + CubeMap cubeMap = ((CubeMapGetter) chunk).getCubeMap(); + if (!cubeMap.isLoaded(cubePos.getY())) { + // This is probably only happening because we don't have load order fixed yet + System.out.println(cube.getCubePos() + " : Cube not in cubemap during sky lighting"); + } + + Heightmap heightmap = ((LightHeightmapGetter) chunk).getLightHeightmap(); + if (heightmap == null) { + System.out.println("heightmap null"); + return; + } + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + int height = heightmap.getFirstAvailable(x, z); + if (height <= maxY) { + height = Math.max(height, minY); + for (int y = maxY; y >= height; y--) { + long pos = new BlockPos((chunkPos.x + sectionX) * 16 + x, y, (chunkPos.z + sectionZ) * 16 + z).asLong(); + // Not sure if this is necessary + ((LayerLightSectionStorageAccess) this.storage).invokeRunAllUpdates(); + + if (((LayerLightSectionStorageAccess) this.storage).invokeStoringLightForSection(SectionPos.blockToSection(pos))) { + addEmissionAtPos(pos); + } + } + } + } + } + } + } + } + + /** + * @author CursedFlames + * @reason disable vanilla sky light logic + */ + @Inject(method = "checkNeighborsAfterUpdate", at = @At("HEAD"), cancellable = true) + private void onCheckNeighborsAfterUpdate(long blockPos, int level, boolean decrease, CallbackInfo ci) { + if (!isCubic) { + return; + } + ci.cancel(); + + long sectionPos = SectionPos.blockToSection(blockPos); + + for (Direction direction : DIRECTIONS) { + long offsetBlockPos = BlockPos.offset(blockPos, direction); + long offsetSectionPos = SectionPos.blockToSection(offsetBlockPos); + // Check all neighbors that are storing light + if (sectionPos == offsetSectionPos || (((LayerLightSectionStorageAccess) this.storage)).invokeStoringLightForSection(offsetSectionPos)) { + this.checkNeighbor(blockPos, offsetBlockPos, level, decrease); + } + } + } + +// /** +// * @author CursedFlames +// * @reason prevent infinite downwards skylight propagation +// */ +// @Inject(method = "computeLevelFromNeighbor", at = @At("RETURN"), cancellable = true) +// private void onComputeLevelFromNeighbor(long sourceId, long targetId, int level, CallbackInfoReturnable cir) { +// if (isCubic && cir.getReturnValue() == 0) { +// cir.setReturnValue(1); +// } +// } +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinSkyLightSectionStorage.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinSkyLightSectionStorage.java new file mode 100644 index 000000000..7c68aeeb7 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinSkyLightSectionStorage.java @@ -0,0 +1,132 @@ +package io.github.opencubicchunks.cubicchunks.mixin.core.common.world.lighting; + +import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; +import io.github.opencubicchunks.cubicchunks.mixin.access.common.LayerLightSectionStorageAccess; +import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; +import io.github.opencubicchunks.cubicchunks.utils.Coords; +import io.github.opencubicchunks.cubicchunks.world.lighting.ICubeLightProvider; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.DataLayer; +import net.minecraft.world.level.chunk.LightChunkGetter; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.lighting.LayerLightEngine; +import net.minecraft.world.level.lighting.LayerLightSectionStorage; +import net.minecraft.world.level.lighting.SkyLightSectionStorage; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(SkyLightSectionStorage.class) +public abstract class MixinSkyLightSectionStorage extends LayerLightSectionStorage { + private boolean isCubic; + + private MixinSkyLightSectionStorage(LightLayer lightLayer, LightChunkGetter lightChunkGetter, + SkyLightSectionStorage.SkyDataLayerStorageMap dataLayerStorageMap) { + super(lightLayer, lightChunkGetter, dataLayerStorageMap); + } + + @Inject(method = "", at = @At("RETURN")) + private void onInit(LightChunkGetter lightChunkGetter, CallbackInfo ci) { + isCubic = ((CubicLevelHeightAccessor) lightChunkGetter.getLevel()).isCubic(); + } + + @Inject(method = "getLightValue(JZ)I", cancellable = true, at = @At("HEAD")) + private void onGetLightValue(long blockPos, boolean cached, CallbackInfoReturnable cir) { + if (!isCubic) { + return; + } + + // Replace this method with an equivalent of BlockLightSectionStorage.getLightValue, + // since we don't need sky light logic + long l = SectionPos.blockToSection(blockPos); + DataLayer dataLayer = this.getDataLayer(l, cached); + int x = BlockPos.getX(blockPos); + int y = BlockPos.getY(blockPos); + int z = BlockPos.getZ(blockPos); + if (dataLayer == null) { + + BlockGetter chunk = ((LayerLightSectionStorageAccess) this).getChunkSource().getChunkForLighting(Coords.blockToSection(x), Coords.blockToSection(z)); + + if (chunk == null) { + // ToDo Known bug: Cubes may be sent to the client ahead of their chunks. This is not fatal. + // see MixinSkyLightEngine.onGetComputedLevel(...) + System.out.println("getLightValue: Missing chunk for lighting."); + // Set return value to prevent vanilla behaviour from happening and causing weird effects + cir.setReturnValue(0); + return; + } + + //TODO: Optimize + BlockGetter cube = ((ICubeLightProvider) ((LayerLightSectionStorageAccess) this).getChunkSource()).getCubeForLighting( + Coords.blockToSection(x), Coords.blockToSection(y), Coords.blockToSection(z)); + if (cube == null || !((IBigCube) cube).getStatus().isOrAfter(ChunkStatus.LIGHT)) { + cir.setReturnValue(0); + return; + } + + Heightmap lightHeightmap = ((LightHeightmapGetter) chunk).getLightHeightmap(); + int height = lightHeightmap.getFirstAvailable(SectionPos.sectionRelative(x), SectionPos.sectionRelative(z)); + cir.setReturnValue(height <= y ? 15 : 0); + } else { + cir.setReturnValue(dataLayer.get( + SectionPos.sectionRelative(x), + SectionPos.sectionRelative(y), + SectionPos.sectionRelative(z))); + } + } + + @Inject(method = "onNodeAdded", cancellable = true, at = @At("HEAD")) + private void onOnNodeAdded(long sectionPos, CallbackInfo ci) { + if (!isCubic) return; + ci.cancel(); + } + + @Inject(method = "onNodeRemoved", cancellable = true, at = @At("HEAD")) + private void onOnNodeRemoved(long sectionPos, CallbackInfo ci) { + if (!isCubic) return; + ci.cancel(); + } + + @Inject(method = "enableLightSources", cancellable = true, + at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/level/lighting/SkyLightSectionStorage;runAllUpdates()V")) + private void onEnableLightSources(long columnPos, boolean enabled, CallbackInfo ci) { + if (!isCubic) return; + if (enabled) { + // We handle skylight emission differently anyway, so we don't need vanilla's sky light source system + ci.cancel(); + } + } + + @Inject(method = "createDataLayer", cancellable = true, at = @At("HEAD")) + private void onCreateDataLayer(long sectionPos, CallbackInfoReturnable cir) { + if (!isCubic) return; + cir.setReturnValue(super.createDataLayer(sectionPos)); + } + + @Inject(method = "markNewInconsistencies", cancellable = true, at = @At("HEAD")) + private void onMarkNewInconsistencies(LayerLightEngine lightProvider, boolean doSkylight, boolean skipEdgeLightPropagation, + CallbackInfo ci) { + if (!isCubic) return; + ci.cancel(); + super.markNewInconsistencies(lightProvider, doSkylight, skipEdgeLightPropagation); + } + + @Inject(method = "hasSectionsBelow", cancellable = true, at = @At("HEAD")) + private void onHasSectionsBelow(int sectionY, CallbackInfoReturnable cir) { + if (!isCubic) return; + cir.setReturnValue(true); + } + + @Inject(method = "isAboveData", cancellable = true, at = @At("HEAD")) + private void onIsAboveData(long sectionPos, CallbackInfoReturnable cir) { + if (!isCubic) return; + cir.setReturnValue(false); + } +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/server/MixinServerWorldLightManager.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinThreadedLevelLightEngine.java similarity index 87% rename from src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/server/MixinServerWorldLightManager.java rename to src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinThreadedLevelLightEngine.java index 1ae1b5e53..f091660a7 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/server/MixinServerWorldLightManager.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/core/common/world/lighting/MixinThreadedLevelLightEngine.java @@ -1,4 +1,4 @@ -package io.github.opencubicchunks.cubicchunks.mixin.core.common.world.server; +package io.github.opencubicchunks.cubicchunks.mixin.core.common.world.lighting; import java.util.concurrent.CompletableFuture; import java.util.function.IntSupplier; @@ -7,11 +7,11 @@ import com.mojang.datafixers.util.Pair; import io.github.opencubicchunks.cubicchunks.CubicChunks; +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; import io.github.opencubicchunks.cubicchunks.chunk.IChunkManager; import io.github.opencubicchunks.cubicchunks.chunk.ticket.CubeTaskPriorityQueueSorter; import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; -import io.github.opencubicchunks.cubicchunks.mixin.core.common.world.lighting.MixinWorldLightManager; import io.github.opencubicchunks.cubicchunks.server.CubicLevelHeightAccessor; import io.github.opencubicchunks.cubicchunks.utils.Coords; import io.github.opencubicchunks.cubicchunks.world.server.IServerWorldLightManager; @@ -22,6 +22,7 @@ import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ThreadedLevelLightEngine; import net.minecraft.util.thread.ProcessorHandle; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.chunk.DataLayer; import net.minecraft.world.level.chunk.LevelChunkSection; @@ -33,7 +34,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ThreadedLevelLightEngine.class) -public abstract class MixinServerWorldLightManager extends MixinWorldLightManager implements IServerWorldLightManager { +public abstract class MixinThreadedLevelLightEngine extends MixinLevelLightEngine implements IServerWorldLightManager { private ProcessorHandle> cubeSorterMailbox; @@ -44,6 +45,8 @@ public abstract class MixinServerWorldLightManager extends MixinWorldLightManage @Shadow private volatile int taskPerBatch; @Shadow protected abstract void runUpdate(); + @Shadow + protected abstract void addTask(int x, int z, ThreadedLevelLightEngine.TaskType stage, Runnable task); @Override public void postConstructorSetup(CubeTaskPriorityQueueSorter sorter, ProcessorHandle> taskExecutor) { @@ -134,19 +137,36 @@ public CompletableFuture lightCube(IBigCube icube, boolean flagIn) { super.onBlockEmissionIncrease(blockPos, icube.getLightEmission(blockPos)); } }); + // FIXME we probably want another flag for controlling skylight + super.doSkyLightForCube(icube); } - ((IChunkManager) this.chunkMap).releaseLightTicket(cubePos); }, () -> "lightCube " + cubePos + " " + flagIn)); return CompletableFuture.supplyAsync(() -> { icube.setCubeLight(true); super.retainData(cubePos, false); + ((IChunkManager) this.chunkMap).releaseLightTicket(cubePos); return icube; }, (runnable) -> { this.addTask(cubePos.getX(), cubePos.getY(), cubePos.getZ(), ThreadedLevelLightEngine.TaskType.POST_UPDATE, runnable); }); } + @Override + public void checkSkyLightColumn(CubeMapGetter chunk, int x, int z, int oldHeight, int newHeight) { + // FIXME figure out when this should actually be scheduled instead of just hoping for the best + this.addTask(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z), ThreadedLevelLightEngine.TaskType.POST_UPDATE, Util.name(() -> { + super.checkSkyLightColumn(chunk, x, z, oldHeight, newHeight); + }, () -> "checkSkyLightColumn " + x + " " + z)); + } + + @Inject(method = "updateChunkStatus", at = @At("HEAD"), cancellable = true) + private void cancelUpdateChunkStatus(ChunkPos pos, CallbackInfo ci) { + if (((CubicLevelHeightAccessor) this.levelHeightAccessor).isCubic()) { + ci.cancel(); + } + } + /** * @author NotStirred * @reason Vanilla lighting is gone diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/client/MixinLevelRenderer.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/client/MixinLevelRenderer.java index 4d92eab6b..869cab43b 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/client/MixinLevelRenderer.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/client/MixinLevelRenderer.java @@ -11,14 +11,13 @@ import com.mojang.math.Matrix4f; import com.mojang.math.Vector3f; import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; -import io.github.opencubicchunks.cubicchunks.chunk.ICubeHolder; import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; +import io.github.opencubicchunks.cubicchunks.client.CubicWorldLoadScreen; import io.github.opencubicchunks.cubicchunks.utils.Coords; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.LevelLoadingScreen; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; @@ -61,7 +60,7 @@ public void render(PoseStack matrices, float tickDelta, long limitTime, boolean bufferBuilder.begin(VertexFormat.Mode.TRIANGLE_STRIP, DefaultVertexFormat.POSITION_COLOR); Object2IntMap colors = getField( - LevelLoadingScreen.class, null, "COLORS" // TODO: intermediary name + CubicWorldLoadScreen.class, null, "STATUS_COLORS" // TODO: intermediary name ); int renderRadius = 5; @@ -85,7 +84,7 @@ public void render(PoseStack matrices, float tickDelta, long limitTime, boolean if (holder == null) { continue; } - ChunkStatus status = ICubeHolder.getCubeStatusFromLevel(holder.getTicketLevel()); + ChunkStatus status = holder.getLastAvailableStatus(); int color = colors.getOrDefault(status, 0); @@ -122,7 +121,7 @@ public void render(PoseStack matrices, float tickDelta, long limitTime, boolean if (holder == null) { continue; } - ChunkStatus status = ICubeHolder.getCubeStatusFromLevel(holder.getTicketLevel()); + ChunkStatus status = holder.getLastAvailableStatus(); int color = colors.getOrDefault(status, 0); @@ -131,14 +130,18 @@ public void render(PoseStack matrices, float tickDelta, long limitTime, boolean int xPos = Coords.cubeToMinBlock(cubeX); int yPos = Coords.cubeToMinBlock(cubeY); int zPos = Coords.cubeToMinBlock(cubeZ); - LevelRenderer.addChainedFilledBoxVertices(bufferBuilder, xPos + 4.25F - cameraX, yPos - 4.25F - cameraY, zPos + 4.25F - cameraZ, - xPos + 11.75F - cameraX, yPos + 4.25F - cameraY, zPos + 11.75F - cameraZ, vector3f.x(), vector3f.y(), vector3f.z(), 1.0F); + drawBox(bufferBuilder, cameraX, cameraY, cameraZ, xPos + 16, yPos + 16, zPos + 16, 4, vector3f); } tesselator.end(); RenderSystem.enableTexture(); } + private void drawBox(BufferBuilder bufferBuilder, double cameraX, double cameraY, double cameraZ, float x, float y, float z, float radius, Vector3f color) { + LevelRenderer.addChainedFilledBoxVertices(bufferBuilder, x - radius - cameraX, y - radius - cameraY, z - radius - cameraZ, + x + radius - cameraX, y + radius - cameraY, z + radius - cameraZ, color.x(), color.y(), color.z(), 1.0F); + } + private static T getField(Class cl, Object obj, String name) { try { Field f = cl.getDeclaredField(name); diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinDistanceManager.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinDistanceManager.java index 59eb7ec8d..5e4a21d58 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinDistanceManager.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinDistanceManager.java @@ -1,5 +1,6 @@ package io.github.opencubicchunks.cubicchunks.mixin.debug.common; +import io.github.opencubicchunks.cubicchunks.chunk.graph.CCTicketType; import net.minecraft.server.level.DistanceManager; import net.minecraft.server.level.Ticket; import net.minecraft.server.level.TicketType; @@ -17,7 +18,7 @@ private void cancelPlayerTickets(long position, Ticket ticket, CallbackInfo c if (!DEBUG_LOAD_ORDER_ENABLED) { return; } - if (ticket.getType() == TicketType.PLAYER) { + if (ticket.getType() == TicketType.PLAYER || ticket.getType() == CCTicketType.CCPLAYER) { ci.cancel(); } } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinMinecraftServer.java b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinMinecraftServer.java index 54a449eaf..7b8cd1a4c 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinMinecraftServer.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/mixin/debug/common/MixinMinecraftServer.java @@ -91,11 +91,11 @@ private void prepareLevels(ChunkProgressListener worldGenerationProgressListener this.waitUntilNextTick(); } - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - int ignored = 0; - } +// try { +// Thread.sleep(10000); +// } catch (InterruptedException e) { +// int ignored = 0; +// } this.nextTickTime = Util.getMillis() + 10L; this.waitUntilNextTick(); diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketHeightmap.java b/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketHeightmap.java index 1771244e7..c5df2d33e 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketHeightmap.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketHeightmap.java @@ -2,6 +2,9 @@ import java.util.Map; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.ClientLightSurfaceTracker; +import io.github.opencubicchunks.cubicchunks.chunk.heightmap.LightSurfaceTrackerWrapper; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.LongArrayTag; import net.minecraft.network.FriendlyByteBuf; @@ -33,6 +36,8 @@ public static PacketHeightmap forChunk(LevelChunk chunk) { heightmaps.put(entry.getKey().getSerializationKey(), new LongArrayTag(entry.getValue().getRawData())); } } + LightSurfaceTrackerWrapper lightHeightmap = ((LightHeightmapGetter) chunk).getServerLightHeightmap(); + heightmaps.put("light", new LongArrayTag(lightHeightmap.getRawData())); return new PacketHeightmap(chunk.getPos(), heightmaps); } @@ -57,6 +62,12 @@ public static void handle(PacketHeightmap packet, Level worldIn) { long[] longArray = packet.heightmaps.getLongArray(value.getSerializationKey()); chunk.setHeightmap(value, longArray); } + if (packet.heightmaps.contains("light")) { + // TODO is this safe on dedicated server? + long[] data = packet.heightmaps.getLongArray("light"); + ClientLightSurfaceTracker heightmap = ((LightHeightmapGetter) chunk).getClientLightHeightmap(); + heightmap.setRawData(data, chunk); + } } } } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketHeightmapChanges.java b/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketHeightmapChanges.java index 9a2f9e35c..cc21005d3 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketHeightmapChanges.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketHeightmapChanges.java @@ -1,6 +1,7 @@ package io.github.opencubicchunks.cubicchunks.network; import io.github.opencubicchunks.cubicchunks.CubicChunks; +import io.github.opencubicchunks.cubicchunks.chunk.LightHeightmapGetter; import io.github.opencubicchunks.cubicchunks.mixin.access.common.HeightmapAccess; import io.github.opencubicchunks.cubicchunks.utils.AddressTools; import it.unimi.dsi.fastutil.shorts.ShortArrayList; @@ -26,8 +27,13 @@ public PacketHeightmapChanges(ChunkAccess chunk, ShortArrayList changed) { for (int i = 0; i < this.positionsAndTypes.length; i++) { int x = AddressTools.getLocalX(positionsAndTypes[i]); int z = AddressTools.getLocalZ(positionsAndTypes[i]); - Heightmap.Types type = Heightmap.Types.values()[AddressTools.getLocalY(positionsAndTypes[i])]; - heights[i] = chunk.getHeight(type, x + dx, z + dz) + 1; + int index = AddressTools.getLocalY(positionsAndTypes[i]); + if (index == 0xF) { // Light heightmap + heights[i] = ((LightHeightmapGetter) chunk).getLightHeightmap().getFirstAvailable(x, z); + } else { // Normal heightmaps + Heightmap.Types type = Heightmap.Types.values()[AddressTools.getLocalY(positionsAndTypes[i])]; + heights[i] = chunk.getHeight(type, x + dx, z + dz) + 1; + } } } @@ -61,8 +67,13 @@ public static void handle(PacketHeightmapChanges packet, Level worldIn) { short posType = packet.positionsAndTypes[i]; int x = AddressTools.getLocalX(posType); int z = AddressTools.getLocalZ(posType); - Heightmap.Types type = Heightmap.Types.values()[AddressTools.getLocalY(posType)]; - ((HeightmapAccess) chunk.getOrCreateHeightmapUnprimed(type)).invokeSetHeight(x & 0xF, z & 0xF, packet.heights[i]); + int index = AddressTools.getLocalY(posType); + if (index == 0xF) { + ((HeightmapAccess) ((LightHeightmapGetter) chunk).getLightHeightmap()).invokeSetHeight(x & 0xF, z & 0xF, packet.heights[i]); + } else { + Heightmap.Types type = Heightmap.Types.values()[index]; + ((HeightmapAccess) chunk.getOrCreateHeightmapUnprimed(type)).invokeSetHeight(x & 0xF, z & 0xF, packet.heights[i]); + } } } } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketUpdateLight.java b/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketUpdateLight.java index bc4b85b6d..71ad251a4 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketUpdateLight.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/network/PacketUpdateLight.java @@ -110,11 +110,7 @@ public static void handle(PacketUpdateLight packet, Level worldIn) { Iterator blockIterator = packet.blockLightData.iterator(); for (int i = 0; i < IBigCube.SECTION_COUNT; ++i) { - SectionPos sectionPos = SectionPos.of( - packet.cubePos.getX() + Coords.indexToX(i), - packet.cubePos.getY() + Coords.indexToY(i), - packet.cubePos.getZ() + Coords.indexToZ(i) - ); + SectionPos sectionPos = Coords.sectionPosByIndex(packet.cubePos, i); if (packet.dataExists.get(i * 2)) { worldlightmanager.queueSectionData(LightLayer.SKY, sectionPos, new DataLayer(skyIterator.next()), packet.lightFlag); diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/server/IServerChunkProvider.java b/src/main/java/io/github/opencubicchunks/cubicchunks/server/IServerChunkProvider.java index a32e4a0ff..c4fe8ff38 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/server/IServerChunkProvider.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/server/IServerChunkProvider.java @@ -9,11 +9,12 @@ import io.github.opencubicchunks.cubicchunks.chunk.util.CubePos; import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.TicketType; -import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; public interface IServerChunkProvider extends ICubeProvider { - ChunkHolder getChunkHolderForce(ChunkPos chunkPos, ChunkStatus requiredStatus); + // TODO check whether this is still needed + // ChunkHolder getChunkHolderForce(ChunkPos chunkPos, ChunkStatus requiredStatus); void addCubeRegionTicket(TicketType type, CubePos pos, int distance, T value); @@ -21,5 +22,9 @@ public interface IServerChunkProvider extends ICubeProvider { void forceCube(CubePos pos, boolean add); + CompletableFuture> getColumnFutureForCube(CubePos cubePos, int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create); + // TODO check whether this is still needed + // boolean isEntityTickingCube(CubePos pos); + boolean checkCubeFuture(long cubePosLong, Function>> futureFunction); } diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/utils/Coords.java b/src/main/java/io/github/opencubicchunks/cubicchunks/utils/Coords.java index 07ddd14bd..e6a53cfb0 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/utils/Coords.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/utils/Coords.java @@ -32,6 +32,7 @@ import net.minecraft.core.SectionPos; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ChunkPos; /** * A class that contains helper-methods for many CubicChunks related things. @@ -377,6 +378,16 @@ public static SectionPos sectionPosByIndex(CubePos cubePos, int i) { indexToZ(i))); } + /** + * @param cubePos The {@link CubePos} + * @param i The index of the {@link ChunkSection} inside the {@link CubePos} + * + * @return The {@link ChunkPos} of the column containing the {@link ChunkSection} at index i + */ + public static ChunkPos chunkPosByIndex(CubePos cubePos, int i) { + return new ChunkPos(cubeToSection(cubePos.getX(), indexToX(i)), cubeToSection(cubePos.getZ(), indexToZ(i))); + } + public static int blockToCubeLocalSection(int x) { return (x >> 4) & (IBigCube.DIAMETER_IN_SECTIONS - 1); diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ICubeLightProvider.java b/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ICubeLightProvider.java index 58601221d..2bd1ae4ed 100644 --- a/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ICubeLightProvider.java +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ICubeLightProvider.java @@ -6,5 +6,6 @@ public interface ICubeLightProvider { + //TODO: FIX THIS NAME AHHHHHHHHHHHHH @Nullable BlockGetter getCubeForLighting(int sectionX, int sectionY, int sectionZ); } \ No newline at end of file diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ICubicSkyLightEngine.java b/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ICubicSkyLightEngine.java new file mode 100644 index 000000000..2bb44bd70 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ICubicSkyLightEngine.java @@ -0,0 +1,7 @@ +package io.github.opencubicchunks.cubicchunks.world.lighting; + +import io.github.opencubicchunks.cubicchunks.chunk.IBigCube; + +public interface ICubicSkyLightEngine { + void doSkyLightForCube(IBigCube cube); +} diff --git a/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ISkyLightColumnChecker.java b/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ISkyLightColumnChecker.java new file mode 100644 index 000000000..d8fb46d60 --- /dev/null +++ b/src/main/java/io/github/opencubicchunks/cubicchunks/world/lighting/ISkyLightColumnChecker.java @@ -0,0 +1,8 @@ +package io.github.opencubicchunks.cubicchunks.world.lighting; + +import io.github.opencubicchunks.cubicchunks.chunk.CubeMapGetter; + +public interface ISkyLightColumnChecker { + /** all parameters are global coordinates */ + void checkSkyLightColumn(CubeMapGetter chunk, int x, int z, int oldHeight, int newHeight); +} diff --git a/src/main/resources/cubicchunks.accesswidener b/src/main/resources/cubicchunks.accesswidener index a5fd3ba14..db3d7e2c0 100644 --- a/src/main/resources/cubicchunks.accesswidener +++ b/src/main/resources/cubicchunks.accesswidener @@ -10,5 +10,6 @@ accessible class net/minecraft/server/level/ThreadedLevelLightEngine$TaskType accessible class net/minecraft/world/level/levelgen/VerticalAnchor$BelowTop accessible class net/minecraft/world/level/levelgen/VerticalAnchor$AboveBottom accessible class net/minecraft/world/level/levelgen/VerticalAnchor$Absolute +accessible class net/minecraft/world/level/lighting/SkyLightSectionStorage$SkyDataLayerStorageMap extendable class net/minecraft/server/level/Ticket accessible method net/minecraft/server/level/ChunkMap$DistanceManager (Lnet/minecraft/server/level/ChunkMap;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)V diff --git a/src/main/resources/cubicchunks.mixins.access.json b/src/main/resources/cubicchunks.mixins.access.json index cecb13f8f..c9f4e577e 100644 --- a/src/main/resources/cubicchunks.mixins.access.json +++ b/src/main/resources/cubicchunks.mixins.access.json @@ -21,6 +21,7 @@ "common.EntityTrackerAccess", "common.HeightmapAccess", "common.IOWorkerAccess", + "common.LayerLightSectionStorageAccess", "common.LevelBasedGraphAccess", "common.Matrix4fAccess", "common.NaturalSpawnerAccess", @@ -30,9 +31,9 @@ "common.PlayerTicketTrackerFactoryAccess", "common.PoiSectionAccess", "common.ProtoTickListAccess", - "common.SectionLightStorageAccess", "common.SpawnStateAccess", "common.StructureFeatureManagerAccess", + "common.ThreadedLevelLightEngineAccess", "common.TicketAccess", "common.TicketManagerAccess", "common.TicketTypeAccess", diff --git a/src/main/resources/cubicchunks.mixins.core.json b/src/main/resources/cubicchunks.mixins.core.json index adfadb490..47fd7c3ec 100644 --- a/src/main/resources/cubicchunks.mixins.core.json +++ b/src/main/resources/cubicchunks.mixins.core.json @@ -23,6 +23,7 @@ "common.chunk.MixinChunkSerializer", "common.chunk.MixinChunkStatus", "common.chunk.MixinDepthBasedReplacingBaseStoneSource", + "common.chunk.MixinImposterProtoChunk", "common.chunk.MixinISelectiveWorker", "common.chunk.MixinNoiseBasedChunkGenerator", "common.chunk.MixinNoiseSampler", @@ -61,9 +62,13 @@ "common.world.feature.nether.MixinTwistyVinesFeature", "common.world.feature.range.MixinRandomPatchFeature", "common.world.lighting.MixinBlockLightEngine", - "common.world.lighting.MixinLightEngine", - "common.world.lighting.MixinSectionLightStorage", - "common.world.lighting.MixinWorldLightManager", + "common.world.lighting.MixinDynamicGraphMinFixedPoint", + "common.world.lighting.MixinLayerLightEngine", + "common.world.lighting.MixinLayerLightSectionStorage", + "common.world.lighting.MixinLevelLightEngine", + "common.world.lighting.MixinSkyLightEngine", + "common.world.lighting.MixinSkyLightSectionStorage", + "common.world.lighting.MixinThreadedLevelLightEngine", "common.world.MixinAbstractChunkProvider", "common.world.MixinConfiguredSurfaceBuilder", "common.world.MixinFuzzyOffsetConstantColumnBiomeZoomer", @@ -88,7 +93,6 @@ "common.world.placement.MixinVerticalDecorator", "common.world.placement.verticalanchor.MixinVerticalAnchorAboveBottom", "common.world.placement.verticalanchor.MixinVerticalAnchorBelowTop", - "common.world.server.MixinServerWorldLightManager", "common.world.structure.MixinConfiguredStructureFeature", "common.world.structure.MixinLocateCommand", "common.world.structure.MixinStructureFeature", @@ -105,6 +109,7 @@ "client.chunk.MixinRenderChunkRegion", "client.debug.MixinChunkBorderRenderer", "client.debug.MixinClientWorld", + "client.debug.MixinDebugScreenOverlay", "client.debug.MixinGameRenderer", "client.debug.MixinMinecraft", "client.debug.MixinMinecraftServer",