Fix update-region expanding with force-updates & chunks with broken tile-entity data can now be loaded

This commit is contained in:
Lukas Rieger (Blue) 2024-06-03 10:17:53 +02:00
parent 7afcbeefd7
commit b5e8bf42ae
No known key found for this signature in database
GPG Key ID: AA33883B1BBA03E6
10 changed files with 112 additions and 8 deletions

View File

@ -44,7 +44,7 @@ public class WebAppImpl implements WebApp {
private final BlueMapService blueMapService; private final BlueMapService blueMapService;
private final @Nullable Plugin plugin; private final @Nullable Plugin plugin;
private final Timer timer = new Timer(); private final Timer timer = new Timer("BlueMap-WebbAppImpl-Timer", true);
private @Nullable TimerTask scheduledWebAppSettingsUpdate; private @Nullable TimerTask scheduledWebAppSettingsUpdate;
public WebAppImpl(BlueMapService blueMapService, @Nullable Plugin plugin) { public WebAppImpl(BlueMapService blueMapService, @Nullable Plugin plugin) {

View File

@ -28,8 +28,10 @@
import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.map.BmMap; import de.bluecolored.bluemap.core.map.BmMap;
import de.bluecolored.bluemap.core.map.renderstate.MapTileState; import de.bluecolored.bluemap.core.map.renderstate.MapTileState;
import de.bluecolored.bluemap.core.map.renderstate.TileInfoRegion;
import de.bluecolored.bluemap.core.map.renderstate.TileState; import de.bluecolored.bluemap.core.map.renderstate.TileState;
import de.bluecolored.bluemap.core.storage.GridStorage; import de.bluecolored.bluemap.core.storage.GridStorage;
import de.bluecolored.bluemap.core.storage.compression.CompressedInputStream;
import de.bluecolored.bluemap.core.util.Grid; import de.bluecolored.bluemap.core.util.Grid;
import de.bluecolored.bluemap.core.world.World; import de.bluecolored.bluemap.core.world.World;
@ -134,6 +136,21 @@ private static Collection<Vector2i> getRegions(BmMap map, Vector2i center, int r
Grid cellGrid = MapTileState.GRID.multiply(tileGrid); Grid cellGrid = MapTileState.GRID.multiply(tileGrid);
try (Stream<GridStorage.Cell> stream = map.getStorage().tileState().stream()) { try (Stream<GridStorage.Cell> stream = map.getStorage().tileState().stream()) {
stream stream
.filter(c -> { // filter out files that are fully UNKNOWN
try (CompressedInputStream in = c.read()) {
if (in == null) return false;
TileState[] states = TileInfoRegion.loadPalette(in.decompress());
for (TileState state : states) {
if (
state != TileState.UNKNOWN &&
state != TileState.NOT_GENERATED
) return true;
}
return false;
} catch (IOException ignore) {
return true;
}
})
.map(c -> new Vector2i(c.getX(), c.getZ())) .map(c -> new Vector2i(c.getX(), c.getZ()))
.flatMap(v -> cellGrid.getIntersecting(v, regionGrid).stream()) .flatMap(v -> cellGrid.getIntersecting(v, regionGrid).stream())
.filter(regionRadiusFilter) .filter(regionRadiusFilter)

View File

@ -24,12 +24,18 @@
*/ */
package de.bluecolored.bluemap.core.map.renderstate; package de.bluecolored.bluemap.core.map.renderstate;
import com.google.gson.reflect.TypeToken;
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluemap.core.util.RegistryAdapter;
import de.bluecolored.bluenbt.BlueNBT;
import de.bluecolored.bluenbt.NBTName; import de.bluecolored.bluenbt.NBTName;
import de.bluecolored.bluenbt.NBTPostDeserialize; import de.bluecolored.bluenbt.NBTPostDeserialize;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -122,4 +128,29 @@ public static TileInfoRegion create() {
return region; return region;
} }
/**
* Only loads the palette-part from a TileState-file
*/
public static TileState[] loadPalette(InputStream in) throws IOException {
return PaletteOnly.BLUE_NBT.read(in, PaletteOnly.class).tileStates.palette;
}
@Getter
private static class PaletteOnly {
private final static BlueNBT BLUE_NBT = new BlueNBT();
static {
BLUE_NBT.register(TypeToken.get(TileState.class), new RegistryAdapter<>(TileState.REGISTRY, Key.BLUEMAP_NAMESPACE, TileState.UNKNOWN));
}
@NBTName("tile-states")
private TileStates tileStates;
@Getter
private static class TileStates {
private TileState[] palette;
}
}
} }

View File

@ -203,7 +203,7 @@ public void accept(int chunkX, int chunkZ, Chunk chunk) {
} }
}); });
} catch (IOException ex) { } catch (IOException ex) {
Logger.global.logDebug("Unexpected exception trying to load preload region (x:" + x + ", z:" + z + "):" + ex); Logger.global.logDebug("Unexpected exception trying to load preload region (x:" + x + ", z:" + z + "): " + ex);
} }
} }
@ -257,7 +257,7 @@ private Chunk loadChunk(int x, int z) {
} }
} }
Logger.global.logDebug("Unexpected exception trying to load chunk (x:" + x + ", z:" + z + "):" + loadException); Logger.global.logDebug("Unexpected exception trying to load chunk (x:" + x + ", z:" + z + "): " + loadException);
return Chunk.ERRORED_CHUNK; return Chunk.ERRORED_CHUNK;
} }

View File

@ -95,8 +95,12 @@ private static class ChunkVersionLoader<D extends MCAChunk.Data> {
private final int dataVersion; private final int dataVersion;
public MCAChunk load(MCAWorld world, InputStream in) throws IOException { public MCAChunk load(MCAWorld world, InputStream in) throws IOException {
D data = MCAUtil.BLUENBT.read(in, dataType); try {
return mightSupport(data.getDataVersion()) ? constructor.apply(world, data) : new MCAChunk(world, data) {}; D data = MCAUtil.BLUENBT.read(in, dataType);
return mightSupport(data.getDataVersion()) ? constructor.apply(world, data) : new MCAChunk(world, data) {};
} catch (Exception e) {
throw new IOException("Failed to parse chunk-data: " + e, e);
}
} }
public boolean mightSupport(int dataVersion) { public boolean mightSupport(int dataVersion) {

View File

@ -33,6 +33,8 @@
import de.bluecolored.bluemap.core.world.block.entity.BlockEntity; import de.bluecolored.bluemap.core.world.block.entity.BlockEntity;
import de.bluecolored.bluemap.core.world.mca.MCAUtil; import de.bluecolored.bluemap.core.world.mca.MCAUtil;
import de.bluecolored.bluemap.core.world.mca.MCAWorld; import de.bluecolored.bluemap.core.world.mca.MCAWorld;
import de.bluecolored.bluemap.core.world.mca.data.LenientBlockEntityArrayDeserializer;
import de.bluecolored.bluenbt.NBTDeserializer;
import de.bluecolored.bluenbt.NBTName; import de.bluecolored.bluenbt.NBTName;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -295,7 +297,10 @@ public static class Level {
private HeightmapsData heightmaps = new HeightmapsData(); private HeightmapsData heightmaps = new HeightmapsData();
private SectionData @Nullable [] sections = null; private SectionData @Nullable [] sections = null;
private int[] biomes = EMPTY_INT_ARRAY; private int[] biomes = EMPTY_INT_ARRAY;
@NBTName("TileEntities") private @Nullable BlockEntity [] blockEntities = EMPTY_BLOCK_ENTITIES_ARRAY;
@NBTName("TileEntities")
@NBTDeserializer(LenientBlockEntityArrayDeserializer.class)
private @Nullable BlockEntity [] blockEntities = EMPTY_BLOCK_ENTITIES_ARRAY;
} }
@Getter @Getter

View File

@ -34,6 +34,8 @@
import de.bluecolored.bluemap.core.world.mca.MCAUtil; import de.bluecolored.bluemap.core.world.mca.MCAUtil;
import de.bluecolored.bluemap.core.world.mca.MCAWorld; import de.bluecolored.bluemap.core.world.mca.MCAWorld;
import de.bluecolored.bluemap.core.world.mca.PackedIntArrayAccess; import de.bluecolored.bluemap.core.world.mca.PackedIntArrayAccess;
import de.bluecolored.bluemap.core.world.mca.data.LenientBlockEntityArrayDeserializer;
import de.bluecolored.bluenbt.NBTDeserializer;
import de.bluecolored.bluenbt.NBTName; import de.bluecolored.bluenbt.NBTName;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -282,7 +284,10 @@ public static class Level {
private HeightmapsData heightmaps = new HeightmapsData(); private HeightmapsData heightmaps = new HeightmapsData();
private SectionData @Nullable [] sections = null; private SectionData @Nullable [] sections = null;
private int[] biomes = EMPTY_INT_ARRAY; private int[] biomes = EMPTY_INT_ARRAY;
@NBTName("TileEntities") private @Nullable BlockEntity [] blockEntities = EMPTY_BLOCK_ENTITIES_ARRAY;
@NBTName("TileEntities")
@NBTDeserializer(LenientBlockEntityArrayDeserializer.class)
private @Nullable BlockEntity [] blockEntities = EMPTY_BLOCK_ENTITIES_ARRAY;
} }
@Getter @Getter

View File

@ -34,6 +34,8 @@
import de.bluecolored.bluemap.core.world.mca.MCAUtil; import de.bluecolored.bluemap.core.world.mca.MCAUtil;
import de.bluecolored.bluemap.core.world.mca.MCAWorld; import de.bluecolored.bluemap.core.world.mca.MCAWorld;
import de.bluecolored.bluemap.core.world.mca.PackedIntArrayAccess; import de.bluecolored.bluemap.core.world.mca.PackedIntArrayAccess;
import de.bluecolored.bluemap.core.world.mca.data.LenientBlockEntityArrayDeserializer;
import de.bluecolored.bluenbt.NBTDeserializer;
import de.bluecolored.bluenbt.NBTName; import de.bluecolored.bluenbt.NBTName;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -290,7 +292,10 @@ public static class Data extends MCAChunk.Data {
private long inhabitedTime = 0; private long inhabitedTime = 0;
private HeightmapsData heightmaps = new HeightmapsData(); private HeightmapsData heightmaps = new HeightmapsData();
private SectionData @Nullable [] sections = null; private SectionData @Nullable [] sections = null;
@NBTName("block_entities") private @Nullable BlockEntity [] blockEntities = EMPTY_BLOCK_ENTITIES_ARRAY;
@NBTName("block_entities")
@NBTDeserializer(LenientBlockEntityArrayDeserializer.class)
private @Nullable BlockEntity [] blockEntities = EMPTY_BLOCK_ENTITIES_ARRAY;
} }
@Getter @Getter

View File

@ -0,0 +1,34 @@
package de.bluecolored.bluemap.core.world.mca.data;
import com.google.gson.reflect.TypeToken;
import de.bluecolored.bluemap.core.world.block.entity.BlockEntity;
import de.bluecolored.bluenbt.BlueNBT;
import de.bluecolored.bluenbt.NBTReader;
import de.bluecolored.bluenbt.TagType;
import de.bluecolored.bluenbt.TypeDeserializer;
import java.io.IOException;
/**
* TypeSerializer that returns a default value instead of failing when the serialized field is of the wrong type
*/
public class LenientBlockEntityArrayDeserializer implements TypeDeserializer<BlockEntity[]> {
private static final BlockEntity[] EMPTY_BLOCK_ENTITIES_ARRAY = new BlockEntity[0];
private final TypeDeserializer<BlockEntity[]> delegate;
public LenientBlockEntityArrayDeserializer(BlueNBT blueNBT) {
delegate = blueNBT.getTypeDeserializer(new TypeToken<>(){});
}
@Override
public BlockEntity[] read(NBTReader reader) throws IOException {
if (reader.peek() != TagType.LIST) {
reader.skip();
return EMPTY_BLOCK_ENTITIES_ARRAY;
}
return delegate.read(reader);
}
}

View File

@ -33,6 +33,7 @@
import de.bluecolored.bluemap.common.config.ConfigurationException; import de.bluecolored.bluemap.common.config.ConfigurationException;
import de.bluecolored.bluemap.common.config.CoreConfig; import de.bluecolored.bluemap.common.config.CoreConfig;
import de.bluecolored.bluemap.common.config.WebserverConfig; import de.bluecolored.bluemap.common.config.WebserverConfig;
import de.bluecolored.bluemap.common.debug.StateDumper;
import de.bluecolored.bluemap.common.plugin.MapUpdateService; import de.bluecolored.bluemap.common.plugin.MapUpdateService;
import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask; import de.bluecolored.bluemap.common.rendermanager.MapUpdateTask;
import de.bluecolored.bluemap.common.rendermanager.RenderManager; import de.bluecolored.bluemap.common.rendermanager.RenderManager;
@ -209,6 +210,8 @@ public void run() {
Runtime.getRuntime().removeShutdownHook(shutdownHook); Runtime.getRuntime().removeShutdownHook(shutdownHook);
shutdown.run(); shutdown.run();
} }
StateDumper.global().dump(Path.of("teststate.json"));
} }
public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOException, ConfigurationException, InterruptedException { public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOException, ConfigurationException, InterruptedException {