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 { private Chunk loadMCA(Instance instance, int chunkX, int chunkZ, ChunkCallback callback) throws IOException, AnvilException {
RegionFile mcaFile = getMCAFile(chunkX, chunkZ); RegionFile mcaFile = getMCAFile(chunkX, chunkZ);
if (mcaFile != null) { if (mcaFile == null)
ChunkColumn fileChunk = mcaFile.getChunk(chunkX, chunkZ); return null;
if (fileChunk != null) { ChunkColumn fileChunk = mcaFile.getChunk(chunkX, chunkZ);
Biome[] biomes; if (fileChunk == null)
if (fileChunk.getGenerationStatus().compareTo(ChunkColumn.GenerationStatus.Biomes) > 0) { return null;
int[] fileChunkBiomes = fileChunk.getBiomes();
biomes = new Biome[fileChunkBiomes.length]; Biome[] biomes;
for (int i = 0; i < fileChunkBiomes.length; i++) { if (fileChunk.getGenerationStatus().compareTo(ChunkColumn.GenerationStatus.Biomes) > 0) {
final int id = fileChunkBiomes[i]; int[] fileChunkBiomes = fileChunk.getBiomes();
biomes[i] = Objects.requireNonNullElse(BIOME_MANAGER.getById(id), BIOME); biomes = new Biome[fileChunkBiomes.length];
} for (int i = 0; i < fileChunkBiomes.length; i++) {
} else { final int id = fileChunkBiomes[i];
biomes = new Biome[1024]; // TODO don't hardcode biomes[i] = Objects.requireNonNullElse(BIOME_MANAGER.getById(id), BIOME);
Arrays.fill(biomes, BIOME); }
} } else {
Chunk chunk = new DynamicChunk(instance, biomes, chunkX, chunkZ); biomes = new Biome[1024]; // TODO don't hardcode
placeBlocks(chunk, fileChunk); Arrays.fill(biomes, BIOME);
loadTileEntities(chunk, fileChunk); }
if (callback != null) { Chunk chunk = new DynamicChunk(instance, biomes, chunkX, chunkZ);
callback.accept(chunk);
} // Blocks
return chunk; {
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) { 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.ChunkDataPacket;
import net.minestom.server.network.packet.server.play.UpdateLightPacket; import net.minestom.server.network.packet.server.play.UpdateLightPacket;
import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.PacketUtils; import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.Position; import net.minestom.server.utils.Position;
import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryReader;
@ -108,9 +109,9 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
@Override @Override
public abstract void setBlock(int x, int y, int z, @NotNull Block block); 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. * Executes a chunk tick.
@ -296,31 +297,53 @@ public abstract class Chunk implements BlockGetter, BlockSetter, Viewable, Ticka
*/ */
@NotNull @NotNull
public UpdateLightPacket getLightPacket() { 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 updateLightPacket = new UpdateLightPacket(getIdentifier(), getLastChangeTime());
updateLightPacket.chunkX = getChunkX(); updateLightPacket.chunkX = getChunkX();
updateLightPacket.chunkZ = getChunkZ(); updateLightPacket.chunkZ = getChunkZ();
final int sectionCount = (getInstance().getDimensionType().getTotalHeight() / 16) + 2; updateLightPacket.skyLightMask = new long[]{skyMask};
final int maskLength = (int) Math.ceil((double) sectionCount / 64); updateLightPacket.blockLightMask = new long[]{blockMask};
updateLightPacket.emptySkyLightMask = new long[]{emptySkyMask};
updateLightPacket.emptyBlockLightMask = new long[]{emptyBlockMask};
updateLightPacket.skyLightMask = new long[maskLength]; updateLightPacket.skyLight = skyLights;
updateLightPacket.blockLightMask = new long[maskLength]; updateLightPacket.blockLight = blockLights;
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);
byte[] bytes = new byte[2048]; emptySkyMask |= 1L << 0;
Arrays.fill(bytes, (byte) 0xFF); emptyBlockMask |= 1L << 0;
final List<byte[]> temp = new ArrayList<>(sectionCount); emptySkyMask |= 1L << 17;
for (int i = 0; i < sectionCount; ++i) { emptyBlockMask |= 1L << 17;
temp.add(bytes);
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; return updateLightPacket;
} }

View File

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