mirror of https://github.com/Minestom/Minestom.git
170 lines
6.6 KiB
Java
170 lines
6.6 KiB
Java
package net.minestom.server.instance.light;
|
|
|
|
import net.minestom.server.coordinate.Vec;
|
|
import net.minestom.server.instance.*;
|
|
import net.minestom.server.instance.block.Block;
|
|
import net.minestom.server.instance.palette.Palette;
|
|
import net.minestom.testing.Env;
|
|
import net.minestom.testing.EnvTest;
|
|
import org.jglrxavpok.hephaistos.mca.AnvilException;
|
|
import org.jglrxavpok.hephaistos.mca.BlockState;
|
|
import org.jglrxavpok.hephaistos.mca.ChunkSection;
|
|
import org.jglrxavpok.hephaistos.mca.RegionFile;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.RandomAccessFile;
|
|
import java.net.URISyntaxException;
|
|
import java.net.URL;
|
|
import java.nio.file.Path;
|
|
import java.util.*;
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
@EnvTest
|
|
public class LightParityIntegrationTest {
|
|
private static final int REGION_SIZE = 3;
|
|
|
|
@Test
|
|
public void test(Env env) throws URISyntaxException, IOException, AnvilException {
|
|
Map<Vec, SectionEntry> sections = retrieveSections();
|
|
// Generate our own light
|
|
|
|
InstanceContainer instance = (InstanceContainer) env.createFlatInstance();
|
|
instance.setChunkSupplier(LightingChunk::new);
|
|
instance.setChunkLoader(new AnvilLoader(Path.of("./src/test/resources/net/minestom/server/instance/lighting")));
|
|
|
|
List<CompletableFuture<Chunk>> futures = new ArrayList<>();
|
|
|
|
int end = REGION_SIZE;
|
|
// Load the chunks
|
|
for (int x = 0; x < end; x++) {
|
|
for (int z = 0; z < end; z++) {
|
|
futures.add(instance.loadChunk(x, z));
|
|
}
|
|
}
|
|
|
|
for (CompletableFuture<Chunk> future : futures) {
|
|
future.join();
|
|
}
|
|
|
|
LightingChunk.relight(instance, instance.getChunks());
|
|
|
|
int differences = 0;
|
|
int differencesZero = 0;
|
|
int blocks = 0;
|
|
int sky = 0;
|
|
|
|
for (Chunk chunk : instance.getChunks()) {
|
|
if (chunk.getChunkX() == 0 || chunk.getChunkZ() == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (chunk.getChunkX() == end - 1 || chunk.getChunkZ() == end - 1) {
|
|
continue;
|
|
}
|
|
|
|
for (int sectionIndex = chunk.getMinSection(); sectionIndex < chunk.getMaxSection(); sectionIndex++) {
|
|
if (sectionIndex > 6) break;
|
|
|
|
Section section = chunk.getSection(sectionIndex);
|
|
|
|
Light sectionLight = section.blockLight();
|
|
Light sectionSkyLight = section.skyLight();
|
|
SectionEntry sectionEntry = sections.get(new Vec(chunk.getChunkX(), sectionIndex, chunk.getChunkZ()));
|
|
if (sectionEntry == null) {
|
|
continue;
|
|
}
|
|
|
|
byte[] serverBlock = sectionLight.array();
|
|
byte[] mcaBlock = sectionEntry.block;
|
|
|
|
byte[] serverSky = sectionSkyLight.array();
|
|
byte[] mcaSky = sectionEntry.sky;
|
|
|
|
for (int x = 0; x < 16; ++x) {
|
|
for (int y = 0; y < 16; ++y) {
|
|
for (int z = 0; z < 16; ++z) {
|
|
int index = x | (z << 4) | (y << 8);
|
|
|
|
{
|
|
int serverBlockValue = LightCompute.getLight(serverBlock, index);
|
|
int mcaBlockValue = mcaBlock.length == 0 ? 0 : LightCompute.getLight(mcaBlock, index);
|
|
|
|
if (serverBlockValue != mcaBlockValue) {
|
|
if (serverBlockValue == 0) differencesZero++;
|
|
else differences++;
|
|
blocks++;
|
|
}
|
|
}
|
|
|
|
// Mojang's sky lighting is wrong
|
|
{
|
|
int serverSkyValue = LightCompute.getLight(serverSky, index);
|
|
int mcaSkyValue = mcaSky.length == 0 ? 0 : LightCompute.getLight(mcaSky, index);
|
|
|
|
if (serverSkyValue != mcaSkyValue) {
|
|
if (serverSkyValue == 0) differencesZero++;
|
|
else differences++;
|
|
sky++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
assertEquals(0, blocks);
|
|
assertEquals(0, sky);
|
|
assertEquals(0, differences);
|
|
assertEquals(0, differencesZero);
|
|
}
|
|
|
|
record SectionEntry(Palette blocks, byte[] sky, byte[] block) {
|
|
}
|
|
|
|
private static Map<Vec, SectionEntry> retrieveSections() throws IOException, URISyntaxException, AnvilException {
|
|
URL defaultImage = LightParityIntegrationTest.class.getResource("/net/minestom/server/instance/lighting/region/r.0.0.mca");
|
|
assert defaultImage != null;
|
|
File imageFile = new File(defaultImage.toURI());
|
|
var regionFile = new RegionFile(new RandomAccessFile(imageFile, "rw"),
|
|
0, 0, -64, 384);
|
|
|
|
Map<Vec, SectionEntry> sections = new HashMap<>();
|
|
// Read from anvil
|
|
for (int x = 1; x < REGION_SIZE - 1; x++) {
|
|
for (int z = 1; z < REGION_SIZE - 1; z++) {
|
|
var chunk = regionFile.getChunk(x, z);
|
|
if (chunk == null) continue;
|
|
|
|
for (int yLevel = chunk.getMinY(); yLevel <= chunk.getMaxY(); yLevel += 16) {
|
|
var section = chunk.getSection((byte) (yLevel/16));
|
|
var palette = loadBlocks(section);
|
|
var sky = section.getSkyLights();
|
|
var block = section.getBlockLights();
|
|
sections.put(new Vec(x, section.getY(), z), new SectionEntry(palette, sky, block));
|
|
}
|
|
}
|
|
}
|
|
return sections;
|
|
}
|
|
|
|
private static Palette loadBlocks(ChunkSection section) throws AnvilException {
|
|
var palette = Palette.blocks();
|
|
for (int x = 0; x < Chunk.CHUNK_SECTION_SIZE; x++) {
|
|
for (int z = 0; z < Chunk.CHUNK_SECTION_SIZE; z++) {
|
|
for (int y = 0; y < Chunk.CHUNK_SECTION_SIZE; y++) {
|
|
final BlockState blockState = section.get(x, y, z);
|
|
String blockName = blockState.getName();
|
|
Block block = Objects.requireNonNull(Block.fromNamespaceId(blockName), blockName)
|
|
.withProperties(blockState.getProperties());
|
|
palette.set(x, y, z, block.stateId());
|
|
}
|
|
}
|
|
}
|
|
return palette;
|
|
}
|
|
} |