Minestom/src/test/java/net/minestom/server/instance/light/LightParityIntegrationTest....

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;
}
}