Initial light support

This commit is contained in:
TheMode 2021-06-21 16:32:46 +02:00
parent d27b1ff1a4
commit 735ea152f9
5 changed files with 116 additions and 51 deletions

View File

@ -57,31 +57,46 @@ public class AnvilLoader implements IChunkLoader {
private Chunk loadMCA(Instance instance, int chunkX, int chunkZ, ChunkCallback callback) throws IOException, AnvilException {
RegionFile mcaFile = getMCAFile(chunkX, chunkZ);
if (mcaFile != null) {
ChunkColumn fileChunk = mcaFile.getChunk(chunkX, chunkZ);
if (fileChunk != null) {
Biome[] biomes;
if (fileChunk.getGenerationStatus().compareTo(ChunkColumn.GenerationStatus.Biomes) > 0) {
int[] fileChunkBiomes = fileChunk.getBiomes();
biomes = new Biome[fileChunkBiomes.length];
for (int i = 0; i < fileChunkBiomes.length; i++) {
final int id = fileChunkBiomes[i];
biomes[i] = Objects.requireNonNullElse(BIOME_MANAGER.getById(id), BIOME);
}
} else {
biomes = new Biome[1024]; // TODO don't hardcode
Arrays.fill(biomes, BIOME);
}
Chunk chunk = new DynamicChunk(instance, biomes, chunkX, chunkZ);
placeBlocks(chunk, fileChunk);
loadTileEntities(chunk, fileChunk);
if (callback != null) {
callback.accept(chunk);
}
return chunk;
if (mcaFile == null)
return null;
ChunkColumn fileChunk = mcaFile.getChunk(chunkX, chunkZ);
if (fileChunk == null)
return null;
Biome[] biomes;
if (fileChunk.getGenerationStatus().compareTo(ChunkColumn.GenerationStatus.Biomes) > 0) {
int[] fileChunkBiomes = fileChunk.getBiomes();
biomes = new Biome[fileChunkBiomes.length];
for (int i = 0; i < fileChunkBiomes.length; i++) {
final int id = fileChunkBiomes[i];
biomes[i] = Objects.requireNonNullElse(BIOME_MANAGER.getById(id), BIOME);
}
} else {
biomes = new Biome[1024]; // TODO don't hardcode
Arrays.fill(biomes, BIOME);
}
Chunk chunk = new DynamicChunk(instance, biomes, chunkX, chunkZ);
// Blocks
{
placeBlocks(chunk, fileChunk);
loadTileEntities(chunk, fileChunk);
}
// Lights
{
final var chunkSections = fileChunk.getSections();
for (var chunkSection : chunkSections) {
Section section = chunk.getSection(chunkSection.getY());
section.setSkyLight(chunkSection.getSkyLights());
section.setBlockLight(chunkSection.getBlockLights());
}
}
return null;
if (callback != null) {
callback.accept(chunk);
}
return chunk;
}
private RegionFile getMCAFile(int chunkX, int chunkZ) {

View File

@ -16,6 +16,7 @@ import net.minestom.server.instance.block.BlockSetter;
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
import net.minestom.server.network.packet.server.play.UpdateLightPacket;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.binary.BinaryReader;
@ -108,9 +109,9 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
@Override
public abstract void setBlock(int x, int y, int z, @NotNull Block block);
public abstract @NotNull Map<Integer, Section> getSections();
public abstract @NotNull TreeMap<Integer, Section> getSections();
public abstract @Nullable Section getSection(int section);
public abstract @NotNull Section getSection(int section);
/**
* Executes a chunk tick.
@ -296,31 +297,53 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
*/
@NotNull
public UpdateLightPacket getLightPacket() {
// TODO do not hardcode light
long skyMask = 0;
long blockMask = 0;
long emptySkyMask = 0;
long emptyBlockMask = 0;
List<byte[]> skyLights = new ArrayList<>();
List<byte[]> blockLights = new ArrayList<>();
// Creates a light packet for the given number of sections with all block light at max and no sky light.
UpdateLightPacket updateLightPacket = new UpdateLightPacket(getIdentifier(), getLastChangeTime());
updateLightPacket.chunkX = getChunkX();
updateLightPacket.chunkZ = getChunkZ();
final int sectionCount = (getInstance().getDimensionType().getTotalHeight() / 16) + 2;
final int maskLength = (int) Math.ceil((double) sectionCount / 64);
updateLightPacket.skyLightMask = new long[]{skyMask};
updateLightPacket.blockLightMask = new long[]{blockMask};
updateLightPacket.emptySkyLightMask = new long[]{emptySkyMask};
updateLightPacket.emptyBlockLightMask = new long[]{emptyBlockMask};
updateLightPacket.skyLightMask = new long[maskLength];
updateLightPacket.blockLightMask = new long[maskLength];
updateLightPacket.emptySkyLightMask = new long[maskLength];
updateLightPacket.emptyBlockLightMask = new long[maskLength];
// Set all block light and no sky light
Arrays.fill(updateLightPacket.blockLightMask, -1L);
Arrays.fill(updateLightPacket.emptySkyLightMask, -1L);
updateLightPacket.skyLight = skyLights;
updateLightPacket.blockLight = blockLights;
byte[] bytes = new byte[2048];
Arrays.fill(bytes, (byte) 0xFF);
final List<byte[]> temp = new ArrayList<>(sectionCount);
for (int i = 0; i < sectionCount; ++i) {
temp.add(bytes);
emptySkyMask |= 1L << 0;
emptyBlockMask |= 1L << 0;
emptySkyMask |= 1L << 17;
emptyBlockMask |= 1L << 17;
final var sections = getSections();
for (var entry : sections.entrySet()) {
final int index = entry.getKey() + 1;
final Section section = entry.getValue();
final var skyLight = section.getSkyLight();
final var blockLight = section.getBlockLight();
if (!ArrayUtils.empty(skyLight)) {
skyLights.add(skyLight);
skyMask |= 1L << index;
} else {
emptySkyMask |= 1L << index;
}
if (!ArrayUtils.empty(blockLight)) {
blockLights.add(blockLight);
blockMask |= 1L << index;
} else {
emptyBlockMask |= 1L << index;
}
}
updateLightPacket.blockLight = temp;
return updateLightPacket;
}

View File

@ -2,7 +2,6 @@ package net.minestom.server.instance;
import com.extollit.gaming.ai.path.model.ColumnarOcclusionFieldList;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import it.unimi.dsi.fastutil.objects.Object2ShortMap;
import it.unimi.dsi.fastutil.objects.Object2ShortOpenHashMap;
import net.minestom.server.MinecraftServer;
@ -28,6 +27,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Represents a {@link Chunk} which store each individual block in memory.
@ -42,7 +42,7 @@ public class DynamicChunk extends Chunk {
*/
private static final int DATA_FORMAT_VERSION = 1;
protected final Int2ObjectRBTreeMap<Section> sectionMap = new Int2ObjectRBTreeMap<>();
protected final TreeMap<Integer, Section> sectionMap = new TreeMap<>();
// Key = ChunkUtils#getBlockIndex
protected final Int2ObjectOpenHashMap<BlockHandler> handlerMap = new Int2ObjectOpenHashMap<>();
@ -94,13 +94,13 @@ public class DynamicChunk extends Chunk {
}
@Override
public @NotNull Map<Integer, Section> getSections() {
public @NotNull TreeMap<Integer, Section> getSections() {
return sectionMap;
}
@Override
public @Nullable Section getSection(int section) {
return sectionMap.get(section);
public @NotNull Section getSection(int section) {
return sectionMap.computeIfAbsent(section, key -> new Section());
}
@Override
@ -348,7 +348,7 @@ public class DynamicChunk extends Chunk {
packet.biomes = biomes;
packet.chunkX = chunkX;
packet.chunkZ = chunkZ;
packet.sections = sectionMap.clone(); // TODO deep clone
packet.sections = (Map<Integer, Section>) sectionMap.clone(); // TODO deep clone
packet.handlerMap = handlerMap.clone();
packet.nbtMap = nbtMap.clone();
@ -361,8 +361,8 @@ public class DynamicChunk extends Chunk {
@Override
public Chunk copy(@NotNull Instance instance, int chunkX, int chunkZ) {
DynamicChunk dynamicChunk = new DynamicChunk(instance, biomes.clone(), chunkX, chunkZ);
for (var entry : sectionMap.int2ObjectEntrySet()) {
dynamicChunk.sectionMap.put(entry.getIntKey(), entry.getValue().clone());
for (var entry : sectionMap.entrySet()) {
dynamicChunk.sectionMap.put(entry.getKey(), entry.getValue().clone());
}
dynamicChunk.handlerMap.putAll(handlerMap);
dynamicChunk.nbtMap.putAll(nbtMap);
@ -379,6 +379,6 @@ public class DynamicChunk extends Chunk {
private @NotNull Section retrieveSection(int y) {
final int sectionIndex = ChunkUtils.getSectionAt(y);
return sectionMap.computeIfAbsent(sectionIndex, key -> new Section());
return getSection(sectionIndex);
}
}

View File

@ -8,6 +8,9 @@ public class Section implements PublicCloneable<Section> {
private final Palette palette;
private byte[] skyLight = new byte[0];
private byte[] blockLight = new byte[0];
private Section(Palette palette) {
this.palette = palette;
}
@ -28,6 +31,22 @@ public class Section implements PublicCloneable<Section> {
palette.setBlockAt(x, y, z, blockId);
}
public byte[] getSkyLight() {
return skyLight;
}
public void setSkyLight(byte[] skyLight) {
this.skyLight = skyLight;
}
public byte[] getBlockLight() {
return blockLight;
}
public void setBlockLight(byte[] blockLight) {
this.blockLight = blockLight;
}
public void clean() {
palette.clean();
}

View File

@ -117,4 +117,12 @@ public final class ArrayUtils {
}
}
public static boolean empty(byte[] array) {
for (byte b : array) {
if (b != 0) {
return false;
}
}
return true;
}
}