Fix several lighting issues on high and lowres renders

This commit is contained in:
Blue (Lukas Rieger) 2021-09-13 19:44:31 +02:00
parent 31e35ce205
commit eadf854c50
No known key found for this signature in database
GPG Key ID: 904C4995F9E1F800
22 changed files with 219 additions and 411 deletions

View File

@ -32,11 +32,9 @@
import de.bluecolored.bluemap.core.debug.DebugDump;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.mca.MCAWorld;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import de.bluecolored.bluemap.core.world.SlicedWorld;
import de.bluecolored.bluemap.core.world.World;
import org.apache.commons.io.FileUtils;
@ -156,7 +154,7 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc
World world = worlds.get(worldUUID);
if (world == null) {
try {
world = MCAWorld.load(worldFolder.toPath(), worldUUID, worldNameProvider.apply(worldUUID), mapConfig.isIgnoreMissingLightData());
world = MCAWorld.load(worldFolder.toPath(), worldUUID, worldNameProvider.apply(worldUUID), 15, mapConfig.isIgnoreMissingLightData());
worlds.put(worldUUID, world);
} catch (MissingResourcesException e) {
throw e; // rethrow this to stop loading and display resource-missing message
@ -165,19 +163,6 @@ private synchronized void loadWorldsAndMaps() throws IOException, InterruptedExc
continue;
}
}
//slice world if configured
if (!mapConfig.getMin().equals(RenderSettings.DEFAULT_MIN) || !mapConfig.getMax().equals(RenderSettings.DEFAULT_MAX)) {
if (mapConfig.isRenderEdges()) {
world = new SlicedWorld(world, mapConfig.getMin(), mapConfig.getMax());
} else {
world = new SlicedWorld(
world,
mapConfig.getMin().min(mapConfig.getMin().sub(2, 2, 2)), // protect from int-overflow
mapConfig.getMax().max(mapConfig.getMax().add(2, 2, 2)) // protect from int-overflow
);
}
}
BmMap map = new BmMap(
id,

View File

@ -94,7 +94,7 @@ private static List<Vector2i> getRegions(World world) {
}
private static List<Vector2i> getRegions(World world, Vector2i center, int radius) {
if (center == null || radius < 0) return new ArrayList<>(world.listRegions());
if (center == null || radius < 0) return new ArrayList<>(world.listRegions()); //TODO: remove regions outside render-boundaries
List<Vector2i> regions = new ArrayList<>();

View File

@ -115,7 +115,9 @@ public void doWork() {
}
//Logger.global.logInfo("Working on " + worldRegion + " - Tile " + tile);
map.renderTile(tile); // <- actual work
if (isAllChunksInTileGenerated(tile)) {
map.renderTile(tile); // <- actual work
}
synchronized (this) {
this.atWork--;
@ -126,6 +128,22 @@ public void doWork() {
}
}
private boolean isAllChunksInTileGenerated(Vector2i tile) {
Grid tileGrid = map.getHiresModelManager().getTileGrid();
Grid chunkGrid = map.getWorld().getChunkGrid();
Vector2i minChunk = tileGrid.getCellMin(tile, chunkGrid);
Vector2i maxChunk = tileGrid.getCellMax(tile, chunkGrid);
for (int x = minChunk.getX(); x <= maxChunk.getX(); x++) {
for (int z = minChunk.getY(); z <= maxChunk.getY(); z++) {
if (!map.getWorld().getChunk(x, z).isGenerated()) return false;
}
}
return true;
}
private void complete() {
map.getRenderState().setRenderTime(worldRegion, startTime);

View File

@ -45,6 +45,7 @@ public class MapConfig implements MapSettings {
private Vector2i startPos;
private int skyColor;
private float ambientLight;
private int worldSkyLight;
private boolean renderCaves;
@ -82,6 +83,9 @@ public MapConfig(ConfigurationNode node) throws IOException {
//ambientLight
this.ambientLight = node.node("ambientLight").getFloat(0f);
//worldSkyLight
this.worldSkyLight = node.node("worldSkyLight").getInt(15);
//renderCaves
this.renderCaves = node.node("renderCaves").getBoolean(false);
@ -101,7 +105,7 @@ public MapConfig(ConfigurationNode node) throws IOException {
//useCompression
this.useGzip = node.node("useCompression").getBoolean(true);
//ignoreMissingLightData
this.ignoreMissingLightData = node.node("ignoreMissingLightData").getBoolean(false);
@ -135,11 +139,17 @@ public Vector2i getStartPos() {
public int getSkyColor() {
return skyColor;
}
@Override
public float getAmbientLight() {
return ambientLight;
}
@Override
public int getWorldSkyLight() {
return worldSkyLight;
}
public boolean isRenderCaves() {
return renderCaves;
}

View File

@ -1,126 +0,0 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.debug;
import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3i;
import de.bluecolored.bluemap.core.world.Chunk;
import de.bluecolored.bluemap.core.world.Grid;
import de.bluecolored.bluemap.core.world.Region;
import de.bluecolored.bluemap.core.world.World;
import java.nio.file.Path;
import java.util.Collection;
import java.util.UUID;
public class OneBlockWorld implements World {
private final World delegate;
public OneBlockWorld(World delegate) {
this.delegate = delegate;
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public UUID getUUID() {
return delegate.getUUID();
}
@Override
public Path getSaveFolder() {
return delegate.getSaveFolder();
}
@Override
public int getSeaLevel() {
return 64;
}
@Override
public Vector3i getSpawnPoint() {
return new Vector3i(0, 70, 0);
}
@Override
public int getMaxY(int x, int z) {
return 255;
}
@Override
public int getMinY(int x, int z) {
return 0;
}
@Override
public Grid getChunkGrid() {
return delegate.getChunkGrid();
}
@Override
public Grid getRegionGrid() {
return delegate.getRegionGrid();
}
@Override
public Chunk getChunkAtBlock(int x, int y, int z) {
return delegate.getChunkAtBlock(x, y, z);
}
@Override
public Chunk getChunk(int x, int z) {
return delegate.getChunk(x, z);
}
@Override
public Region getRegion(int x, int z) {
return delegate.getRegion(x, z);
}
@Override
public Collection<Vector2i> listRegions() {
return delegate.listRegions();
}
@Override
public void invalidateChunkCache() {
delegate.invalidateChunkCache();
}
@Override
public void invalidateChunkCache(int x, int z) {
delegate.invalidateChunkCache(x, z);
}
@Override
public void cleanUpChunkCache() {
delegate.cleanUpChunkCache();
}
}

View File

@ -55,7 +55,7 @@ public HiresTileMeta render(World world, Vector3i modelMin, Vector3i modelMax, H
int maxHeight, minY, maxY;
Color columnColor = new Color(), blockColor = new Color();
BlockNeighborhood<?> block = new BlockNeighborhood<>(resourcePack, world, 0, 0, 0);
BlockNeighborhood<?> block = new BlockNeighborhood<>(resourcePack, renderSettings, world, 0, 0, 0);
BlockModelView blockModel = new BlockModelView(model);
int x, y, z;
@ -65,35 +65,39 @@ public HiresTileMeta render(World world, Vector3i modelMin, Vector3i modelMax, H
maxHeight = 0;
columnColor.set(0, 0, 0, 1, true);
minY = Math.max(min.getY(), world.getMinY(x, z));
maxY = Math.min(max.getY(), world.getMaxY(x, z));
if (renderSettings.isInsideRenderBoundaries(x, z)) {
minY = Math.max(min.getY(), world.getMinY(x, z));
maxY = Math.min(max.getY(), world.getMaxY(x, z));
for (y = minY; y <= maxY; y++){
block.set(x, y, z);
blockColor.set(0, 0, 0, 0, true);
blockModel.initialize();
for (y = minY; y <= maxY; y++) {
block.set(x, y, z);
if (!block.isInsideRenderBounds()) continue;
blockColor.set(0, 0, 0, 0, true);
blockModel.initialize();
try {
modelFactory.render(block, blockModel, blockColor);
} catch (NoSuchResourceException e) {
try {
modelFactory.render(block, BlockState.MISSING, blockModel.reset(), blockColor);
} catch (NoSuchResourceException e2) {
e.addSuppressed(e2);
modelFactory.render(block, blockModel, blockColor);
} catch (NoSuchResourceException e) {
try {
modelFactory.render(block, BlockState.MISSING, blockModel.reset(), blockColor);
} catch (NoSuchResourceException e2) {
e.addSuppressed(e2);
}
//Logger.global.noFloodDebug(block.getBlockState().getFullId() + "-hiresModelRenderer-blockmodelerr", "Failed to create BlockModel for BlockState: " + block.getBlockState() + " (" + e.toString() + ")");
}
//Logger.global.noFloodDebug(block.getBlockState().getFullId() + "-hiresModelRenderer-blockmodelerr", "Failed to create BlockModel for BlockState: " + block.getBlockState() + " (" + e.toString() + ")");
}
// skip empty blocks
if (blockModel.getSize() <= 0) continue;
// skip empty blocks
if (blockModel.getSize() <= 0) continue;
// move block-model to correct position
blockModel.translate(x - modelAnchor.getX(), y - modelAnchor.getY(), z - modelAnchor.getZ());
//update color and height (only if not 100% translucent)
if (blockColor.a > 0) {
maxHeight = y;
columnColor.overlay(blockColor);
// move block-model to correct position
blockModel.translate(x - modelAnchor.getX(), y - modelAnchor.getY(), z - modelAnchor.getZ());
//update color and height (only if not 100% translucent)
if (blockColor.a > 0) {
maxHeight = y;
columnColor.overlay(blockColor);
}
}
}

View File

@ -52,6 +52,9 @@ default Vector3i getMax() {
return DEFAULT_MAX;
}
float getAmbientLight();
int getWorldSkyLight();
/**
* The same as the maximum height, but blocks that are above this value are treated as AIR.<br>
@ -67,5 +70,29 @@ default boolean isRenderEdges() {
default boolean useGzipCompression() {
return true;
}
default boolean isInsideRenderBoundaries(int x, int z) {
Vector3i min = getMin();
Vector3i max = getMax();
return
x >= min.getX() &&
x <= max.getX() &&
z >= min.getZ() &&
z <= max.getZ();
}
default boolean isInsideRenderBoundaries(int x, int y, int z) {
Vector3i min = getMin();
Vector3i max = getMax();
return
x >= min.getX() &&
x <= max.getX() &&
z >= min.getZ() &&
z <= max.getZ() &&
y >= min.getY() &&
y <= max.getY();
}
}

View File

@ -40,7 +40,7 @@
import de.bluecolored.bluemap.core.util.math.VectorM3f;
import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.ResourcePackBlock;
import de.bluecolored.bluemap.core.world.ExtendedBlock;
/**
* A model builder for all liquid blocks
@ -144,10 +144,11 @@ private void build() {
blockColor.multiply(tintcolor);
// apply light
float sl = block.getSunLightLevel() / 16f;
blockColor.r *= sl;
blockColor.g *= sl;
blockColor.b *= sl;
float combinedLight = Math.max(block.getSunLightLevel() / 15f, block.getBlockLightLevel() / 15f);
combinedLight = (renderSettings.getAmbientLight() + combinedLight) / (renderSettings.getAmbientLight() + 1f);
blockColor.r *= combinedLight;
blockColor.g *= combinedLight;
blockColor.b *= combinedLight;
} else {
blockColor.set(0, 0, 0, 0, true);
}
@ -166,7 +167,7 @@ private float getLiquidCornerHeight(int x, int z){
float sumHeight = 0f;
int count = 0;
ResourcePackBlock<?> neighbor;
ExtendedBlock<?> neighbor;
BlockState neighborBlockState;
for (ix = x; ix <= x+1; ix++){
@ -197,7 +198,7 @@ private boolean isLiquidBlockingBlock(BlockState blockState){
return !blockState.equals(BlockState.AIR);
}
private boolean isSameLiquid(ResourcePackBlock<?> block){
private boolean isSameLiquid(ExtendedBlock<?> block){
if (block.getBlockState().getFullId().equals(this.blockState.getFullId())) return true;
return this.blockState.isWater() && (block.getBlockState().isWaterlogged() || block.getProperties().isAlwaysWaterlogged());
}
@ -217,7 +218,7 @@ private boolean createElementFace(Direction faceDir, VectorM3f c0, VectorM3f c1,
Vector3i faceDirVector = faceDir.toVector();
//face culling
ResourcePackBlock<?> bl = block.getNeighborBlock(
ExtendedBlock<?> bl = block.getNeighborBlock(
faceDirVector.getX(),
faceDirVector.getY(),
faceDirVector.getZ()
@ -341,7 +342,7 @@ private int getFlowingAngle() {
}
private float compareLiquidHeights(float ownHeight, int dx, int dz) {
ResourcePackBlock<?> neighbor = block.getNeighborBlock(dx, 0, dz);
ExtendedBlock<?> neighbor = block.getNeighborBlock(dx, 0, dz);
if (neighbor.getBlockState().isAir()) return 0;
if (!isSameLiquid(neighbor)) return 0;

View File

@ -44,7 +44,7 @@
import de.bluecolored.bluemap.core.util.math.VectorM3f;
import de.bluecolored.bluemap.core.world.BlockNeighborhood;
import de.bluecolored.bluemap.core.world.LightData;
import de.bluecolored.bluemap.core.world.ResourcePackBlock;
import de.bluecolored.bluemap.core.world.ExtendedBlock;
/**
* This model builder creates a BlockStateModel using the information from parsed resource-pack json files.
@ -160,12 +160,12 @@ private void createElementFace(BlockModelResource.Element element, Direction fac
// face culling
if (face.getCullface() != null) {
ResourcePackBlock<?> b = getRotationRelativeBlock(face.getCullface());
ExtendedBlock<?> b = getRotationRelativeBlock(face.getCullface());
if (b.getProperties().isCulling()) return;
}
// light calculation
ResourcePackBlock<?> facedBlockNeighbor = getRotationRelativeBlock(faceDir);
ExtendedBlock<?> facedBlockNeighbor = getRotationRelativeBlock(faceDir);
LightData blockLightData = block.getLightData();
LightData facedLightData = facedBlockNeighbor.getLightData();
@ -307,20 +307,21 @@ private void createElementFace(BlockModelResource.Element element, Direction fac
}
// apply light
float sl = sunLight / 16f;
mapColor.r *= sl;
mapColor.g *= sl;
mapColor.b *= sl;
float combinedLight = Math.max(sunLight / 15f, blockLight / 15f);
combinedLight = (renderSettings.getAmbientLight() + combinedLight) / (renderSettings.getAmbientLight() + 1f);
mapColor.r *= combinedLight;
mapColor.g *= combinedLight;
mapColor.b *= combinedLight;
blockColor.add(mapColor);
}
}
private ResourcePackBlock<?> getRotationRelativeBlock(Direction direction){
private ExtendedBlock<?> getRotationRelativeBlock(Direction direction){
return getRotationRelativeBlock(direction.toVector());
}
private ResourcePackBlock<?> getRotationRelativeBlock(Vector3i direction){
private ExtendedBlock<?> getRotationRelativeBlock(Vector3i direction){
return getRotationRelativeBlock(
direction.getX(),
direction.getY(),
@ -329,7 +330,7 @@ private ResourcePackBlock<?> getRotationRelativeBlock(Vector3i direction){
}
private final VectorM3f rotationRelativeBlockDirection = new VectorM3f(0, 0, 0);
private ResourcePackBlock<?> getRotationRelativeBlock(int dx, int dy, int dz){
private ExtendedBlock<?> getRotationRelativeBlock(int dx, int dy, int dz){
rotationRelativeBlockDirection.set(dx, dy, dz);
makeRotationRelative(rotationRelativeBlockDirection);

View File

@ -42,8 +42,8 @@ public class ChunkAnvil113 extends MCAChunk {
private int[] biomes;
@SuppressWarnings("unchecked")
public ChunkAnvil113(CompoundTag chunkTag, boolean ignoreMissingLightData) {
super(chunkTag);
public ChunkAnvil113(MCAWorld world, CompoundTag chunkTag) {
super(world, chunkTag);
CompoundTag levelData = chunkTag.getCompoundTag("Level");
@ -51,7 +51,7 @@ public ChunkAnvil113(CompoundTag chunkTag, boolean ignoreMissingLightData) {
this.isGenerated = status.equals("full");
this.hasLight = isGenerated;
if (!isGenerated && ignoreMissingLightData) {
if (!isGenerated && getWorld().isIgnoreMissingLightData()) {
isGenerated = !status.equals("empty");
}
@ -103,14 +103,14 @@ public BlockState getBlockState(int x, int y, int z) {
@Override
public LightData getLightData(int x, int y, int z, LightData target) {
if (!hasLight) return target.set(15, 0);
if (!hasLight) return target.set(getWorld().getSkyLight(), 0);
int sectionY = y >> 4;
if (sectionY < 0 || sectionY >= this.sections.length)
return (y < 0) ? target.set(0, 0) : target.set(15, 0);
return (y < 0) ? target.set(0, 0) : target.set(getWorld().getSkyLight(), 0);
Section section = this.sections[sectionY];
if (section == null) return target.set(15, 0);
if (section == null) return target.set(getWorld().getSkyLight(), 0);
return section.getLightData(x, y, z, target);
}

View File

@ -42,8 +42,8 @@ public class ChunkAnvil115 extends MCAChunk {
private int[] biomes;
@SuppressWarnings("unchecked")
public ChunkAnvil115(CompoundTag chunkTag, boolean ignoreMissingLightData) {
super(chunkTag);
public ChunkAnvil115(MCAWorld world, CompoundTag chunkTag) {
super(world, chunkTag);
CompoundTag levelData = chunkTag.getCompoundTag("Level");
@ -51,7 +51,7 @@ public ChunkAnvil115(CompoundTag chunkTag, boolean ignoreMissingLightData) {
this.isGenerated = status.equals("full");
this.hasLight = isGenerated;
if (!isGenerated && ignoreMissingLightData) {
if (!isGenerated && getWorld().isIgnoreMissingLightData()) {
isGenerated = !status.equals("empty");
}
@ -103,14 +103,14 @@ public BlockState getBlockState(int x, int y, int z) {
@Override
public LightData getLightData(int x, int y, int z, LightData target) {
if (!hasLight) return target.set(15, 0);
if (!hasLight) return target.set(getWorld().getSkyLight(), 0);
int sectionY = y >> 4;
if (sectionY < 0 || sectionY >= this.sections.length)
return (y < 0) ? target.set(0, 0) : target.set(15, 0);
return (y < 0) ? target.set(0, 0) : target.set(getWorld().getSkyLight(), 0);
Section section = this.sections[sectionY];
if (section == null) return target.set(15, 0);
if (section == null) return target.set(getWorld().getSkyLight(), 0);
return section.getLightData(x, y, z, target);
}

View File

@ -46,8 +46,8 @@ public class ChunkAnvil116 extends MCAChunk {
private int[] biomes;
@SuppressWarnings("unchecked")
public ChunkAnvil116(CompoundTag chunkTag, boolean ignoreMissingLightData) {
super(chunkTag);
public ChunkAnvil116(MCAWorld world, CompoundTag chunkTag) {
super(world, chunkTag);
CompoundTag levelData = chunkTag.getCompoundTag("Level");
@ -55,7 +55,7 @@ public ChunkAnvil116(CompoundTag chunkTag, boolean ignoreMissingLightData) {
this.isGenerated = status.equals("full");
this.hasLight = isGenerated;
if (!isGenerated && ignoreMissingLightData) {
if (!isGenerated && getWorld().isIgnoreMissingLightData()) {
isGenerated = !status.equals("empty");
}
@ -119,12 +119,12 @@ public BlockState getBlockState(int x, int y, int z) {
@Override
public LightData getLightData(int x, int y, int z, LightData target) {
if (!hasLight) return target.set(15, 0);
if (!hasLight) return target.set(getWorld().getSkyLight(), 0);
int sectionY = y >> 4;
Section section = getSection(sectionY);
if (section == null) return (sectionY < sectionMin) ? target.set(0, 0) : target.set(15, 0);
if (section == null) return (sectionY < sectionMin) ? target.set(0, 0) : target.set(getWorld().getSkyLight(), 0);
return section.getLightData(x, y, z, target);
}

View File

@ -33,13 +33,21 @@
public abstract class MCAChunk implements Chunk {
private final MCAWorld world;
private final int dataVersion;
protected MCAChunk() {
this.world = null;
this.dataVersion = -1;
}
protected MCAChunk(MCAWorld world) {
this.world = world;
this.dataVersion = -1;
}
protected MCAChunk(CompoundTag chunkTag) {
protected MCAChunk(MCAWorld world, CompoundTag chunkTag) {
this.world = world;
dataVersion = chunkTag.getInt("DataVersion");
}
@ -69,13 +77,17 @@ public int getMaxY(int x, int z) {
public int getMinY(int x, int z) {
return 0;
}
public static MCAChunk create(MCAWorld world, CompoundTag chunkTag, boolean ignoreMissingLightData) throws IOException {
protected MCAWorld getWorld() {
return world;
}
public static MCAChunk create(MCAWorld world, CompoundTag chunkTag) throws IOException {
int version = chunkTag.getInt("DataVersion");
if (version < 2200) return new ChunkAnvil113(chunkTag, ignoreMissingLightData);
if (version < 2500) return new ChunkAnvil115(chunkTag, ignoreMissingLightData);
return new ChunkAnvil116(chunkTag, ignoreMissingLightData);
if (version < 2200) return new ChunkAnvil113(world, chunkTag);
if (version < 2500) return new ChunkAnvil115(world, chunkTag);
return new ChunkAnvil116(world, chunkTag);
}
public static MCAChunk empty() {
@ -85,6 +97,7 @@ public static MCAChunk empty() {
@Override
public String toString() {
return "MCAChunk{" +
"world=" + world +
"dataVersion=" + dataVersion +
"isGenerated()=" + isGenerated() +
'}';

View File

@ -84,7 +84,7 @@ public MCAChunk loadChunk(int chunkX, int chunkZ, boolean ignoreMissingLightData
DataInputStream dis = new DataInputStream(new BufferedInputStream(compressionType.decompress(new FileInputStream(raf.getFD()))));
Tag<?> tag = Tag.deserialize(dis, Tag.DEFAULT_MAX_DEPTH);
if (tag instanceof CompoundTag) {
MCAChunk chunk = MCAChunk.create(world, (CompoundTag) tag, ignoreMissingLightData);
MCAChunk chunk = MCAChunk.create(world, (CompoundTag) tag);
if (!chunk.isGenerated()) return MCAChunk.empty();
return chunk;
} else {

View File

@ -54,6 +54,7 @@ public class MCAWorld implements World {
@DebugDump private final String name;
@DebugDump private final Vector3i spawnPoint;
@DebugDump private final int skyLight;
@DebugDump private final boolean ignoreMissingLightData;
private final LoadingCache<Vector2i, MCARegion> regionCache;
@ -64,13 +65,15 @@ private MCAWorld(
UUID uuid,
String name,
Vector3i spawnPoint,
int skyLight,
boolean ignoreMissingLightData
) {
this.uuid = uuid;
this.worldFolder = worldFolder;
this.name = name;
this.spawnPoint = spawnPoint;
this.skyLight = skyLight;
this.ignoreMissingLightData = ignoreMissingLightData;
this.regionCache = Caffeine.newBuilder()
@ -152,8 +155,8 @@ public Path getSaveFolder() {
}
@Override
public int getSeaLevel() {
return 63;
public int getSkyLight() {
return skyLight;
}
@Override
@ -203,7 +206,11 @@ public Path getWorldFolder() {
private Path getRegionFolder() {
return worldFolder.resolve("region");
}
public boolean isIgnoreMissingLightData() {
return ignoreMissingLightData;
}
private File getMCAFile(int regionX, int regionZ) {
return getRegionFolder().resolve("r." + regionX + "." + regionZ + ".mca").toFile();
}
@ -248,12 +255,8 @@ private MCAChunk loadChunk(int x, int z) {
Logger.global.logDebug("Unexpected exception trying to load chunk (x:" + x + ", z:" + z + "):" + loadException);
return MCAChunk.empty();
}
public static MCAWorld load(Path worldFolder, UUID uuid) throws IOException {
return load(worldFolder, uuid, null, false);
}
public static MCAWorld load(Path worldFolder, UUID uuid, String name, boolean ignoreMissingLightData) throws IOException {
public static MCAWorld load(Path worldFolder, UUID uuid, String name, int skyLight, boolean ignoreMissingLightData) throws IOException {
try {
StringBuilder subDimensionName = new StringBuilder();
@ -292,6 +295,7 @@ public static MCAWorld load(Path worldFolder, UUID uuid, String name, boolean ig
uuid,
name,
spawnPoint,
skyLight,
ignoreMissingLightData
);
} catch (ClassCastException | NullPointerException ex) {

View File

@ -44,7 +44,7 @@ public static OutputStream createFilepartOutputStream(final Path file) throws IO
try {
Files.move(partFile, file, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException ex) {
} catch (IOException ex) {
Files.move(partFile, file);
}
});

View File

@ -24,30 +24,31 @@
*/
package de.bluecolored.bluemap.core.world;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
public class BlockNeighborhood<T extends BlockNeighborhood<T>> extends ResourcePackBlock<T> {
public class BlockNeighborhood<T extends BlockNeighborhood<T>> extends ExtendedBlock<T> {
private static final int DIAMETER = 8;
private static final int DIAMETER_MASK = DIAMETER - 1;
private static final int DIAMETER_SQUARED = DIAMETER * DIAMETER;
private final ResourcePackBlock<?>[] neighborhood;
private final ExtendedBlock<?>[] neighborhood;
private int thisIndex;
public BlockNeighborhood(ResourcePackBlock<?> center) {
super(center.getResourcePack(), null, 0, 0, 0);
public BlockNeighborhood(ExtendedBlock<?> center) {
super(center.getResourcePack(), center.getRenderSettings(), null, 0, 0, 0);
copy(center);
neighborhood = new ResourcePackBlock[DIAMETER * DIAMETER * DIAMETER];
neighborhood = new ExtendedBlock[DIAMETER * DIAMETER * DIAMETER];
init();
}
public BlockNeighborhood(ResourcePack resourcePack, World world, int x, int y, int z) {
super(resourcePack, world, x, y, z);
public BlockNeighborhood(ResourcePack resourcePack, RenderSettings renderSettings, World world, int x, int y, int z) {
super(resourcePack, renderSettings, world, x, y, z);
neighborhood = new ResourcePackBlock[DIAMETER * DIAMETER * DIAMETER];
neighborhood = new ExtendedBlock[DIAMETER * DIAMETER * DIAMETER];
init();
}
@ -61,11 +62,11 @@ protected void reset() {
private void init() {
this.thisIndex = -1;
for (int i = 0; i < neighborhood.length; i++) {
neighborhood[i] = new ResourcePackBlock<>(this.getResourcePack(), null, 0, 0, 0);
neighborhood[i] = new ExtendedBlock<>(this.getResourcePack(), this.getRenderSettings(), null, 0, 0, 0);
}
}
public ResourcePackBlock<?> getNeighborBlock(int dx, int dy, int dz) {
public ExtendedBlock<?> getNeighborBlock(int dx, int dy, int dz) {
int i = neighborIndex(dx, dy, dz);
if (i == thisIndex()) return this;
return neighborhood[i].set(

View File

@ -24,19 +24,22 @@
*/
package de.bluecolored.bluemap.core.world;
import de.bluecolored.bluemap.core.map.hires.RenderSettings;
import de.bluecolored.bluemap.core.resourcepack.ResourcePack;
import java.util.Objects;
public class ResourcePackBlock<T extends ResourcePackBlock<T>> extends Block<T> {
public class ExtendedBlock<T extends ExtendedBlock<T>> extends Block<T> {
private final ResourcePack resourcePack;
private final RenderSettings renderSettings;
private BlockProperties properties;
private Biome biome;
private Boolean insideRenderBounds;
public ResourcePackBlock(ResourcePack resourcePack, World world, int x, int y, int z) {
public ExtendedBlock(ResourcePack resourcePack, RenderSettings renderSettings, World world, int x, int y, int z) {
super(world, x, y, z);
this.resourcePack = Objects.requireNonNull(resourcePack);
this.renderSettings = renderSettings;
}
@Override
@ -45,6 +48,20 @@ protected void reset() {
this.properties = null;
this.biome = null;
this.insideRenderBounds = null;
}
@Override
public BlockState getBlockState() {
if (!isInsideRenderBounds() && renderSettings.isRenderEdges()) return BlockState.AIR;
return super.getBlockState();
}
@Override
public LightData getLightData() {
LightData ld = super.getLightData();
if (!isInsideRenderBounds() && renderSettings.isRenderEdges()) ld.set(getWorld().getSkyLight(), ld.getBlockLight());
return ld;
}
public BlockProperties getProperties() {
@ -57,6 +74,15 @@ public Biome getBiome() {
return biome;
}
public RenderSettings getRenderSettings() {
return renderSettings;
}
public boolean isInsideRenderBounds() {
if (insideRenderBounds == null) insideRenderBounds = renderSettings.isInsideRenderBoundaries(getX(), getY(), getZ());
return insideRenderBounds;
}
public ResourcePack getResourcePack() {
return resourcePack;
}

View File

@ -1,157 +0,0 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.core.world;
import com.flowpowered.math.vector.Vector2i;
import com.flowpowered.math.vector.Vector3i;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
/**
* A sliced view of a world. Everything outside the defined bounds is seen as "not generated" and "air".
*/
public class SlicedWorld implements World {
private final World world;
private final Vector3i min;
private final Vector3i max;
public SlicedWorld(World world, Vector3i min, Vector3i max) {
this.world = world;
this.min = min;
this.max = max;
}
@Override
public String getName() {
return world.getName();
}
@Override
public Path getSaveFolder() {
return world.getSaveFolder();
}
@Override
public UUID getUUID() {
return world.getUUID();
}
@Override
public int getSeaLevel() {
return world.getSeaLevel();
}
@Override
public Vector3i getSpawnPoint() {
return world.getSpawnPoint();
}
@Override
public int getMaxY(int x, int z) {
return world.getMaxY(x, z);
}
@Override
public int getMinY(int x, int z) {
return world.getMinY(x, z);
}
@Override
public Grid getChunkGrid() {
return world.getChunkGrid();
}
@Override
public Grid getRegionGrid() {
return world.getRegionGrid();
}
@Override
public Chunk getChunk(int x, int z) {
return world.getChunk(x, z);
}
@Override
public Chunk getChunkAtBlock(int x, int y, int z) {
return world.getChunkAtBlock(x, y, z);
}
@Override
public Region getRegion(int x, int z) {
return world.getRegion(x, z);
}
@Override
public Collection<Vector2i> listRegions() {
Grid regionGrid = getRegionGrid();
Collection<Vector2i> regions = new ArrayList<>();
for (Vector2i region : world.listRegions()) {
Vector2i min = regionGrid.getCellMin(region);
Vector2i max = regionGrid.getCellMax(region);
if (isInside(min.getX(), min.getY()) || isInside(max.getX(), max.getY())) regions.add(region);
}
return regions;
}
@Override
public void invalidateChunkCache() {
world.invalidateChunkCache();
}
@Override
public void invalidateChunkCache(int x, int z) {
world.invalidateChunkCache(x, z);
}
@Override
public void cleanUpChunkCache() {
world.cleanUpChunkCache();
}
private boolean isInside(int x, int z) {
return
x >= min.getX() &&
x <= max.getX() &&
z >= min.getZ() &&
z <= max.getZ();
}
private boolean isInside(int x, int y, int z) {
return
x >= min.getX() &&
x <= max.getX() &&
z >= min.getZ() &&
z <= max.getZ() &&
y >= min.getY() &&
y <= max.getY();
}
}

View File

@ -43,8 +43,8 @@ public interface World {
UUID getUUID();
Path getSaveFolder();
int getSeaLevel();
int getSkyLight();
Vector3i getSpawnPoint();

View File

@ -68,7 +68,7 @@ public Collection<SpongeCommandProxy> getRootCommands(){
public class SpongeCommandProxy implements Command.Raw {
private String label;
private final String label;
protected SpongeCommandProxy(String label) {
this.label = label;
@ -88,12 +88,13 @@ public CommandResult process(CommandCause cause, ArgumentReader.Mutable argument
try {
return CommandResult.builder().result(dispatcher.execute(command, cause)).build();
} catch (CommandSyntaxException ex) {
cause.audience().sendMessage(Component.text(ex.getRawMessage().getString(), NamedTextColor.RED));
Component errText = Component.text(ex.getRawMessage().getString(), NamedTextColor.RED);
String context = ex.getContext();
if (context != null) cause.audience().sendMessage(Component.text(context, NamedTextColor.GRAY));
if (context != null)
errText = errText.append(Component.newline()).append(Component.text(context, NamedTextColor.GRAY));
return CommandResult.empty();
return CommandResult.error(errText);
}
}

View File

@ -36,9 +36,10 @@
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.resourcepack.ParseResourceException;
import de.bluecolored.bluemap.sponge8.SpongeCommands.SpongeCommandProxy;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.querz.nbt.CompoundTag;
import net.querz.nbt.NBTUtil;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.spongepowered.api.Platform;
import org.spongepowered.api.Server;
import org.spongepowered.api.Sponge;
@ -65,7 +66,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@org.spongepowered.plugin.jvm.Plugin(Plugin.PLUGIN_ID)
@org.spongepowered.plugin.builtin.jvm.Plugin(Plugin.PLUGIN_ID)
public class SpongePlugin implements ServerInterface {
private final PluginContainer pluginContainer;
@ -92,13 +93,12 @@ public SpongePlugin(org.apache.logging.log4j.Logger logger, PluginContainer plug
this.onlinePlayerMap = new ConcurrentHashMap<>();
this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>());
final String versionFromSponge = Sponge.platform().container(Platform.Component.GAME).metadata().version();
MinecraftVersion version = new MinecraftVersion(1, 16);
try {
version = MinecraftVersion.of(versionFromSponge);
} catch (IllegalArgumentException e) {
Logger.global.logWarning("Failed to find a matching version for version-name '" + versionFromSponge + "'! Using latest known sponge-version: " + version.getVersionString());
}
final ArtifactVersion versionFromSponge = Sponge.platform().container(Platform.Component.GAME).metadata().version();
MinecraftVersion version = new MinecraftVersion(
versionFromSponge.getMajorVersion(),
versionFromSponge.getMinorVersion(),
versionFromSponge.getIncrementalVersion()
);
this.pluginInstance = new Plugin(version, "sponge-8.0.0", this);
this.commands = new SpongeCommands(pluginInstance);
@ -210,7 +210,7 @@ public String getWorldName(UUID worldUUID) {
serverWorld -> serverWorld
.properties()
.displayName()
.map(PlainComponentSerializer.plain()::serialize)
.map(PlainTextComponentSerializer.plainText()::serialize)
)
.orElse(null);
}