Improve Version-Management and add support for variable world-heights to be able to support custom 1.17 worlds

This commit is contained in:
Blue (Lukas Rieger) 2021-06-04 11:25:05 +02:00
parent ecef2c4996
commit 67a0e2a0bf
No known key found for this signature in database
GPG Key ID: 904C4995F9E1F800
11 changed files with 101 additions and 66 deletions

View File

@ -36,7 +36,7 @@ public class MinecraftVersion implements Comparable<MinecraftVersion> {
private static final Pattern VERSION_REGEX = Pattern.compile("(?:(?<major>\\d+)\\.(?<minor>\\d+))(?:\\.(?<patch>\\d+))?(?:\\-(?:pre|rc)\\d+)?");
public static final MinecraftVersion LATEST_SUPPORTED = new MinecraftVersion(1, 16, 5);
public static final MinecraftVersion LATEST_SUPPORTED = new MinecraftVersion(1, 17, 0);
public static final MinecraftVersion EARLIEST_SUPPORTED = new MinecraftVersion(1, 12, 2);
public static final MinecraftVersion THE_FLATTENING = new MinecraftVersion(1, 13);
@ -145,7 +145,8 @@ public class MinecraftVersion implements Comparable<MinecraftVersion> {
MC_1_14 (new MinecraftVersion(1, 14), "mc1_13", "https://launcher.mojang.com/v1/objects/8c325a0c5bd674dd747d6ebaa4c791fd363ad8a9/client.jar"),
MC_1_15 (new MinecraftVersion(1, 15), "mc1_15", "https://launcher.mojang.com/v1/objects/e3f78cd16f9eb9a52307ed96ebec64241cc5b32d/client.jar"),
MC_1_16 (new MinecraftVersion(1, 16), "mc1_16", "https://launcher.mojang.com/v1/objects/228fdf45541c4c2fe8aec4f20e880cb8fcd46621/client.jar"),
MC_1_16_2 (new MinecraftVersion(1, 16, 2), "mc1_16", "https://launcher.mojang.com/v1/objects/653e97a2d1d76f87653f02242d243cdee48a5144/client.jar");
MC_1_16_2 (new MinecraftVersion(1, 16, 2), "mc1_16", "https://launcher.mojang.com/v1/objects/653e97a2d1d76f87653f02242d243cdee48a5144/client.jar"),
MC_1_17_0_PRE (new MinecraftVersion(1, 17, -1), "mc1_16", "https://launcher.mojang.com/v1/objects/de8e68ea23f837f9ab628cda7b16ba3de4b79153/client.jar");
private final MinecraftVersion version;
private final String resourcePrefix;

View File

@ -69,8 +69,8 @@ public class HiresModelManager {
Vector2i tileMin = tileGrid.getCellMin(tile);
Vector2i tileMax = tileGrid.getCellMax(tile);
Vector3i modelMin = new Vector3i(tileMin.getX(), world.getMinY(), tileMin.getY());
Vector3i modelMax = new Vector3i(tileMax.getX(), world.getMaxY(), tileMax.getY());
Vector3i modelMin = new Vector3i(tileMin.getX(), Integer.MIN_VALUE, tileMin.getY());
Vector3i modelMax = new Vector3i(tileMax.getX(), Integer.MAX_VALUE, tileMax.getY());
HiresModel model = renderer.render(world, modelMin, modelMax);
save(model, tile);

View File

@ -58,6 +58,7 @@ public class HiresModelRenderer {
public HiresModel render(World world, Vector3i modelMin, Vector3i modelMax) {
Vector3i min = modelMin.max(renderSettings.getMin());
Vector3i max = modelMax.min(renderSettings.getMax());
Vector3f modelAnchor = new Vector3f(modelMin.getX(), 0, modelMin.getZ());
HiresModel model = new HiresModel(world.getUUID(), modelMin, modelMax);
@ -66,8 +67,11 @@ public class HiresModelRenderer {
int maxHeight = 0;
Vector4f color = Vector4f.ZERO;
for (int y = min.getY(); y <= max.getY(); y++){
int minY = Math.max(min.getY(), world.getMinY(x, z));
int maxY = Math.min(max.getY(), world.getMaxY(x, z));
for (int y = minY; y <= maxY; y++){
Block block = world.getBlock(x, y, z);
if (block.getBlockState().equals(BlockState.AIR)) continue;
@ -83,8 +87,12 @@ public class HiresModelRenderer {
}
//Logger.global.noFloodDebug(block.getBlockState().getFullId() + "-hiresModelRenderer-blockmodelerr", "Failed to create BlockModel for BlockState: " + block.getBlockState() + " (" + e.toString() + ")");
}
blockModel.translate(new Vector3f(x, y, z).sub(modelMin.toFloat()));
// skip empty blocks
if (blockModel.getFaces().isEmpty()) continue;
// move block-model to correct position
blockModel.translate(new Vector3f(x - modelAnchor.getX(), y - modelAnchor.getY(), z - modelAnchor.getZ()));
//update color and height (only if not 100% translucent)
Vector4f blockColor = blockModel.getMapColor();

View File

@ -33,7 +33,6 @@ import de.bluecolored.bluemap.core.world.LightData;
import net.querz.nbt.CompoundTag;
import net.querz.nbt.ListTag;
import net.querz.nbt.NumberTag;
import net.querz.nbt.mca.MCAUtil;
import java.util.Arrays;
import java.util.function.IntFunction;
@ -90,7 +89,8 @@ public class ChunkAnvil112 extends MCAChunk {
@Override
public BlockState getBlockState(Vector3i pos) {
int sectionY = MCAUtil.blockToChunk(pos.getY());
int sectionY = pos.getY() >> 4;
if (sectionY < 0 || sectionY >= this.sections.length) return BlockState.AIR;
Section section = this.sections[sectionY];
if (section == null) return BlockState.AIR;
@ -99,7 +99,8 @@ public class ChunkAnvil112 extends MCAChunk {
}
public String getBlockIdMeta(Vector3i pos) {
int sectionY = MCAUtil.blockToChunk(pos.getY());
int sectionY = pos.getY() >> 4;
if (sectionY < 0 || sectionY >= this.sections.length) return "0:0";
Section section = this.sections[sectionY];
if (section == null) return "0:0";
@ -110,8 +111,10 @@ public class ChunkAnvil112 extends MCAChunk {
@Override
public LightData getLightData(Vector3i pos) {
if (!hasLight) return LightData.SKY;
int sectionY = MCAUtil.blockToChunk(pos.getY());
int sectionY = pos.getY() >> 4;
if (sectionY < 0 || sectionY >= this.sections.length)
return (pos.getY() < 0) ? LightData.ZERO : LightData.SKY;
Section section = this.sections[sectionY];
if (section == null) return LightData.SKY;
@ -124,7 +127,8 @@ public class ChunkAnvil112 extends MCAChunk {
x = x & 0xF; // Math.floorMod(pos.getX(), 16)
z = z & 0xF;
int biomeByteIndex = z * 16 + x;
if (biomeByteIndex >= this.biomes.length) return Biome.DEFAULT;
return biomeIdMapper.get(biomes[biomeByteIndex] & 0xFF);
}

View File

@ -31,7 +31,6 @@ import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
import net.querz.nbt.*;
import net.querz.nbt.mca.MCAUtil;
import java.util.Arrays;
import java.util.HashMap;
@ -99,7 +98,8 @@ public class ChunkAnvil113 extends MCAChunk {
@Override
public BlockState getBlockState(Vector3i pos) {
int sectionY = MCAUtil.blockToChunk(pos.getY());
int sectionY = pos.getY() >> 4;
if (sectionY < 0 || sectionY >= this.sections.length) return BlockState.AIR;
Section section = this.sections[sectionY];
if (section == null) return BlockState.AIR;
@ -110,8 +110,10 @@ public class ChunkAnvil113 extends MCAChunk {
@Override
public LightData getLightData(Vector3i pos) {
if (!hasLight) return LightData.SKY;
int sectionY = MCAUtil.blockToChunk(pos.getY());
int sectionY = pos.getY() >> 4;
if (sectionY < 0 || sectionY >= this.sections.length)
return (pos.getY() < 0) ? LightData.ZERO : LightData.SKY;
Section section = this.sections[sectionY];
if (section == null) return LightData.SKY;
@ -124,7 +126,8 @@ public class ChunkAnvil113 extends MCAChunk {
x = x & 0xF; // Math.floorMod(pos.getX(), 16)
z = z & 0xF;
int biomeIntIndex = z * 16 + x;
if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT;
return biomeIdMapper.get(biomes[biomeIntIndex]);
}

View File

@ -31,7 +31,6 @@ import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
import net.querz.nbt.*;
import net.querz.nbt.mca.MCAUtil;
import java.util.Arrays;
import java.util.HashMap;
@ -99,7 +98,8 @@ public class ChunkAnvil115 extends MCAChunk {
@Override
public BlockState getBlockState(Vector3i pos) {
int sectionY = MCAUtil.blockToChunk(pos.getY());
int sectionY = pos.getY() >> 4;
if (sectionY < 0 || sectionY >= this.sections.length) return BlockState.AIR;
Section section = this.sections[sectionY];
if (section == null) return BlockState.AIR;
@ -110,8 +110,10 @@ public class ChunkAnvil115 extends MCAChunk {
@Override
public LightData getLightData(Vector3i pos) {
if (!hasLight) return LightData.SKY;
int sectionY = MCAUtil.blockToChunk(pos.getY());
int sectionY = pos.getY() >> 4;
if (sectionY < 0 || sectionY >= this.sections.length)
return (pos.getY() < 0) ? LightData.ZERO : LightData.SKY;
Section section = this.sections[sectionY];
if (section == null) return LightData.SKY;
@ -125,7 +127,8 @@ public class ChunkAnvil115 extends MCAChunk {
z = (z & 0xF) / 4;
y = y / 4;
int biomeIntIndex = y * 16 + z * 4 + x;
if (biomeIntIndex >= this.biomes.length) return Biome.DEFAULT;
return biomeIdMapper.get(biomes[biomeIntIndex]);
}

View File

@ -31,7 +31,6 @@ import de.bluecolored.bluemap.core.world.Biome;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.LightData;
import net.querz.nbt.*;
import net.querz.nbt.mca.MCAUtil;
import java.util.Arrays;
import java.util.HashMap;
@ -43,7 +42,8 @@ public class ChunkAnvil116 extends MCAChunk {
private boolean isGenerated;
private boolean hasLight;
private Section[] sections;
private Map<Integer, Section> sections;
private int sectionMin, sectionMax;
private int[] biomes;
@SuppressWarnings("unchecked")
@ -62,11 +62,20 @@ public class ChunkAnvil116 extends MCAChunk {
isGenerated = !status.equals("empty");
}
this.sections = new Section[32]; //32 supports a max world-height of 512 which is the max that the hightmaps of Minecraft V1.13+ can store with 9 bits, i believe?
this.sections = new HashMap<>(); // Is using a has-map the fastest/best way for an int->Object mapping?
this.sectionMin = Integer.MAX_VALUE;
this.sectionMax = Integer.MIN_VALUE;
if (levelData.containsKey("Sections")) {
for (CompoundTag sectionTag : ((ListTag<CompoundTag>) levelData.getListTag("Sections"))) {
if (sectionTag.getListTag("Palette") == null) continue; // ignore empty sections
Section section = new Section(sectionTag);
if (section.getSectionY() >= 0 && section.getSectionY() < sections.length) sections[section.getSectionY()] = section;
int y = section.getSectionY();
if (sectionMin > y) sectionMin = y;
if (sectionMax < y) sectionMax = y;
sections.put(y, section);
}
}
@ -83,12 +92,8 @@ public class ChunkAnvil116 extends MCAChunk {
this.biomes = ((IntArrayTag) tag).getValue();
}
if (biomes == null || biomes.length == 0) {
this.biomes = new int[1024];
}
if (biomes.length < 1024) {
this.biomes = Arrays.copyOf(biomes, 1024);
if (biomes == null) {
this.biomes = new int[0];
}
}
@ -99,9 +104,9 @@ public class ChunkAnvil116 extends MCAChunk {
@Override
public BlockState getBlockState(Vector3i pos) {
int sectionY = MCAUtil.blockToChunk(pos.getY());
int sectionY = pos.getY() >> 4;
Section section = this.sections[sectionY];
Section section = this.sections.get(sectionY);
if (section == null) return BlockState.AIR;
return section.getBlockState(pos);
@ -111,24 +116,40 @@ public class ChunkAnvil116 extends MCAChunk {
public LightData getLightData(Vector3i pos) {
if (!hasLight) return LightData.SKY;
int sectionY = MCAUtil.blockToChunk(pos.getY());
Section section = this.sections[sectionY];
if (section == null) return LightData.SKY;
int sectionY = pos.getY() >> 4;
Section section = this.sections.get(sectionY);
if (section == null) return (sectionY < sectionMin) ? LightData.ZERO : LightData.SKY;
return section.getLightData(pos);
}
@Override
public Biome getBiome(int x, int y, int z) {
if (biomes.length < 16) return Biome.DEFAULT;
x = (x & 0xF) / 4; // Math.floorMod(pos.getX(), 16)
z = (z & 0xF) / 4;
y = y / 4;
int biomeIntIndex = y * 16 + z * 4 + x;
int biomeIntIndex = y * 16 + z * 4 + x; // TODO: fix this for 1.17+ worlds with negative y?
// shift y up/down if not in range
if (biomeIntIndex >= biomes.length) biomeIntIndex -= (((biomeIntIndex - biomes.length) >> 4) + 1) * 16;
if (biomeIntIndex < 0) biomeIntIndex -= (biomeIntIndex >> 4) * 16;
return biomeIdMapper.get(biomes[biomeIntIndex]);
}
@Override
public int getMinY(int x, int z) {
return sectionMin * 16;
}
@Override
public int getMaxY(int x, int z) {
return sectionMax * 16 + 15;
}
private static class Section {
private static final String AIR_ID = "minecraft:air";

View File

@ -57,6 +57,14 @@ public abstract class MCAChunk implements Chunk {
public abstract LightData getLightData(Vector3i pos);
public abstract Biome getBiome(int x, int y, int z);
public int getMaxY(int x, int z) {
return 255;
}
public int getMinY(int x, int z) {
return 0;
}
public static MCAChunk create(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) throws IOException {
int version = chunkTag.getInt("DataVersion");

View File

@ -130,24 +130,11 @@ public class MCAWorld implements World {
@Override
public Biome getBiome(int x, int y, int z) {
if (y < getMinY()) {
y = getMinY();
} else if (y > getMaxY()) {
y = getMaxY();
}
MCAChunk chunk = getChunk(x >> 4, z >> 4);
return chunk.getBiome(x, y, z);
return getChunk(x >> 4, z >> 4).getBiome(x, y, z);
}
@Override
public Block getBlock(Vector3i pos) {
if (pos.getY() < getMinY()) {
return new Block(this, BlockState.AIR, LightData.ZERO, Biome.DEFAULT, BlockProperties.TRANSPARENT, pos);
} else if (pos.getY() > getMaxY()) {
return new Block(this, BlockState.AIR, LightData.SKY, Biome.DEFAULT, BlockProperties.TRANSPARENT, pos);
}
MCAChunk chunk = getChunk(blockToChunk(pos));
BlockState blockState = getExtendedBlockState(chunk, pos);
LightData lightData = chunk.getLightData(pos);
@ -226,13 +213,13 @@ public class MCAWorld implements World {
}
@Override
public int getMinY() {
return 0;
public int getMinY(int x, int z) {
return getChunk(x >> 4, z >> 4).getMinY(x, z);
}
@Override
public int getMaxY() {
return 255;
public int getMaxY(int x, int z) {
return getChunk(x >> 4, z >> 4).getMaxY(x, z);
}
@Override

View File

@ -73,13 +73,13 @@ public class SlicedWorld implements World {
}
@Override
public int getMaxY() {
return world.getMaxY();
public int getMaxY(int x, int z) {
return world.getMaxY(x, z);
}
@Override
public int getMinY() {
return world.getMinY();
public int getMinY(int x, int z) {
return world.getMinY(x, z);
}
@Override

View File

@ -49,9 +49,9 @@ public interface World {
Vector3i getSpawnPoint();
int getMaxY();
int getMaxY(int x, int z);
int getMinY();
int getMinY(int x, int z);
Grid getChunkGrid();