diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java index 4c7078e5..d6c1dbbe 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/commands/Commands.java @@ -56,6 +56,7 @@ import de.bluecolored.bluemap.core.storage.Storage; import de.bluecolored.bluemap.core.world.Chunk; import de.bluecolored.bluemap.core.world.World; import de.bluecolored.bluemap.core.world.block.Block; +import de.bluecolored.bluemap.core.world.block.entity.BlockEntity; import java.io.IOException; import java.nio.file.Path; @@ -528,6 +529,11 @@ public class Commands { lines.put("block-light", block.getBlockLightLevel()); lines.put("sun-light", block.getSunLightLevel()); + BlockEntity blockEntity = block.getBlockEntity(); + if (blockEntity != null) { + lines.put("block-entity", blockEntity); + } + Object[] textElements = lines.entrySet().stream() .flatMap(e -> Stream.of(TextColor.GRAY, e.getKey(), ": ", TextColor.WHITE, e.getValue(), "\n")) .toArray(Object[]::new); diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java index 86b75df4..6a643cdd 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/Chunk.java @@ -24,6 +24,9 @@ */ package de.bluecolored.bluemap.core.world; +import de.bluecolored.bluemap.core.world.block.entity.BlockEntity; +import org.jetbrains.annotations.Nullable; + public interface Chunk { Chunk EMPTY_CHUNK = new Chunk() {}; @@ -72,4 +75,5 @@ public interface Chunk { default int getOceanFloorY(int x, int z) { return 0; } + default @Nullable BlockEntity getBlockEntity(int x, int y, int z) { return null; }; } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/Block.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/Block.java index 476061f9..b9fc9257 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/Block.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/Block.java @@ -28,6 +28,8 @@ import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.Chunk; import de.bluecolored.bluemap.core.world.LightData; import de.bluecolored.bluemap.core.world.World; +import de.bluecolored.bluemap.core.world.block.entity.BlockEntity; +import org.jetbrains.annotations.Nullable; public class Block> { @@ -147,6 +149,10 @@ public class Block> { return getLightData().getBlockLight(); } + public @Nullable BlockEntity getBlockEntity() { + return getChunk().getBlockEntity(x, y, z); + } + @Override public String toString() { if (world != null) { @@ -174,5 +180,4 @@ public class Block> { protected T self() { return (T) this; } - } diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/BannerBlockEntity.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/BannerBlockEntity.java new file mode 100644 index 00000000..940baaaa --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/BannerBlockEntity.java @@ -0,0 +1,86 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * 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.block.entity; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class BannerBlockEntity extends BlockEntity { + private final List patterns = new ArrayList<>(); + + protected BannerBlockEntity(Map data) { + super(data); + + @SuppressWarnings("unchecked") + List> patterns = (List>) data.getOrDefault("Patterns", List.of()); + + for (Map compound : patterns) { + this.patterns.add(new Pattern(compound)); + } + } + + public List getPatterns() { + return patterns; + } + + @Override + public String toString() { + return "BannerBlockEntity{" + + "patterns=" + patterns + + "} " + super.toString(); + } + + public static class Pattern { + private final String code; + private final Color color; + + private Pattern(Map data) { + this.code = (String) data.get("Pattern"); + this.color = Color.values()[(int) data.get("Color")]; + } + + public String getCode() { + return code; + } + + public Color getColor() { + return color; + } + + @Override + public String toString() { + return "Pattern{" + + "code='" + code + '\'' + + ", color=" + color + + '}'; + } + } + + public enum Color { + WHITE, ORANGE, MAGENTA, LIGHT_BLUE, YELLOW, LIME, PINK, GRAY, LIGHT_GRAY, CYAN, PURPLE, BLUE, BROWN, GREEN, + RED, BLACK + } +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/BlockEntity.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/BlockEntity.java new file mode 100644 index 00000000..05757eb4 --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/BlockEntity.java @@ -0,0 +1,137 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * 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.block.entity; + +import com.google.gson.reflect.TypeToken; +import de.bluecolored.bluemap.core.logger.Logger; +import de.bluecolored.bluenbt.BlueNBT; +import de.bluecolored.bluenbt.NBTDeserializer; +import de.bluecolored.bluenbt.NBTReader; +import de.bluecolored.bluenbt.TypeDeserializer; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.StringJoiner; +import java.util.function.Function; +import java.util.stream.Collectors; + +@NBTDeserializer(BlockEntity.BlockEntityDeserializer.class) +public class BlockEntity { + private static final BlueNBT BLUENBT = new BlueNBT(); + + @FunctionalInterface + private interface BlockEntityInitializer { + BlockEntity create(Map data); + } + + @SuppressWarnings("StaticInitializerReferencesSubClass") + private static final Map ID_MAPPING = Map.of( + "minecraft:sign", SignBlockEntity::new, + "minecraft:skull", SkullBlockEntity::new, + "minecraft:banner", BannerBlockEntity::new + ); + + protected final String id; + protected final int x, y, z; + protected final boolean keepPacked; + + protected BlockEntity(Map data) { + this.id = (String) data.get("id"); + this.x = (int) data.get("x"); + this.y = (int) data.get("y"); + this.z = (int) data.get("z"); + this.keepPacked = (byte) data.getOrDefault("keepPacked", (byte) 0) == 1; + } + + public String getId() { + return id; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + public boolean isKeepPacked() { + return keepPacked; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BlockEntity that = (BlockEntity) o; + return x == that.x && y == that.y && z == that.z && keepPacked == that.keepPacked && Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id, x, y, z, keepPacked); + } + + @Override + public String toString() { + return "BlockEntity{" + + "id='" + id + '\'' + + ", x=" + x + + ", y=" + y + + ", z=" + z + + ", keepPacked=" + keepPacked + + '}'; + } + + public static class BlockEntityDeserializer implements TypeDeserializer { + @Override + public BlockEntity read(NBTReader reader) throws IOException { + @SuppressWarnings("unchecked") Map data = + (Map) BLUENBT.read(reader, TypeToken.getParameterized(Map.class, String.class, Object.class)); + + String id = (String) data.get("id"); + if (id == null || id.isBlank()) { + return null; + } + + BlockEntityInitializer instance = ID_MAPPING.getOrDefault(id, BlockEntity::new); + + try { + return instance.create(data); + } catch (Exception e) { + Logger.global.logError("Failed to instantiate BlockEntity instance!", e); + } + + return null; + } + } +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/SignBlockEntity.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/SignBlockEntity.java new file mode 100644 index 00000000..4327987e --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/SignBlockEntity.java @@ -0,0 +1,113 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * 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.block.entity; + +import java.util.List; +import java.util.Map; + +public class SignBlockEntity extends BlockEntity { + private final TextData frontText; + private final TextData backText; + + @SuppressWarnings("unchecked") + protected SignBlockEntity(Map data) { + super(data); + + // Versions before 1.20 used a different format + if (data.containsKey("front_text")) { + this.frontText = new TextData((Map) data.getOrDefault("front_text", Map.of())); + this.backText = new TextData((Map) data.getOrDefault("back_text", Map.of())); + } else { + this.frontText = new TextData( + (byte) data.getOrDefault("GlowingText", (byte) 0) == 1, + (String) data.getOrDefault("Color", ""), + List.of( + (String) data.getOrDefault("Text1", ""), + (String) data.getOrDefault("Text2", ""), + (String) data.getOrDefault("Text3", ""), + (String) data.getOrDefault("Text4", "") + ) + ); + + this.backText = new TextData(false, "", List.of()); + } + } + + public TextData getFrontText() { + return frontText; + } + + public TextData getBackText() { + return backText; + } + + @Override + public String toString() { + return "SignBlockEntity{" + + "frontText=" + frontText + + ", backText=" + backText + + "} " + super.toString(); + } + + public static class TextData { + private final boolean hasGlowingText; + private final String color; + private final List messages; + + @SuppressWarnings("unchecked") + private TextData(Map data) { + this.hasGlowingText = (byte) data.getOrDefault("has_glowing_text", (byte) 0) == 1; + this.color = (String) data.getOrDefault("color", ""); + this.messages = (List) data.getOrDefault("messages", List.of()); + } + + public TextData(boolean hasGlowingText, String color, List messages) { + this.hasGlowingText = hasGlowingText; + this.color = color; + this.messages = messages; + } + + public boolean isHasGlowingText() { + return hasGlowingText; + } + + public String getColor() { + return color; + } + + public List getMessages() { + return messages; + } + + @Override + public String toString() { + return "TextData{" + + "hasGlowingText=" + hasGlowingText + + ", color='" + color + '\'' + + ", messages=" + messages + + '}'; + } + } +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/SkullBlockEntity.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/SkullBlockEntity.java new file mode 100644 index 00000000..33473c8b --- /dev/null +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/block/entity/SkullBlockEntity.java @@ -0,0 +1,137 @@ +/* + * This file is part of BlueMap, licensed under the MIT License (MIT). + * + * Copyright (c) Blue (Lukas Rieger) + * 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.block.entity; + +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public class SkullBlockEntity extends BlockEntity { + private final @Nullable String noteBlockSound; + private final @Nullable String extraType; + private final @Nullable SkullOwner skullOwner; + + protected SkullBlockEntity(Map data) { + super(data); + + this.noteBlockSound = (String) data.get("note_block_sound"); + this.extraType = (String) data.get("ExtraType"); + + @SuppressWarnings("unchecked") + Map ownerData = (Map) data.get("SkullOwner"); + this.skullOwner = ownerData != null ? new SkullOwner(ownerData) : null; + } + + public @Nullable String getNoteBlockSound() { + return noteBlockSound; + } + + public @Nullable String getExtraType() { + return extraType; + } + + public SkullOwner getSkullOwner() { + return skullOwner; + } + + @Override + public String toString() { + return "SkullBlockEntity{" + + "noteBlockSound='" + noteBlockSound + '\'' + + ", extraType='" + extraType + '\'' + + ", skullOwner=" + skullOwner + + "} " + super.toString(); + } + + public static class SkullOwner { + private final @Nullable UUID id; + private final @Nullable String name; + private final List textures = new ArrayList<>(); + + @SuppressWarnings("unchecked") + private SkullOwner(Map data) { + int[] uuidInts = (int[]) data.get("Id"); + this.id = new UUID((long) uuidInts[0] << 32 | uuidInts[1], (long) uuidInts[2] << 32 | uuidInts[3]); + this.name = (String) data.get("Name"); + + Map properties = (Map) data.getOrDefault("Properties", Map.of()); + List> textures = (List>) properties.getOrDefault("textures", List.of()); + + for (Map compound : textures) { + this.textures.add(new Texture(compound)); + } + } + + public UUID getId() { + return id; + } + + public String getName() { + return name; + } + + public List getTextures() { + return textures; + } + + @Override + public String toString() { + return "SkullOwner{" + + "id=" + id + + ", name='" + name + '\'' + + ", textures=" + textures + + '}'; + } + } + + public static class Texture { + private final @Nullable String signature; + private final String value; + + private Texture(Map data) { + this.signature = (String) data.get("signature"); + this.value = (String) data.getOrDefault("value", ""); + } + + public String getSignature() { + return signature; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return "Texture{" + + "signature='" + signature + '\'' + + ", value='" + value + '\'' + + '}'; + } + } +} diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAUtil.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAUtil.java index 9c1a616c..784a4a5e 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAUtil.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/MCAUtil.java @@ -27,6 +27,7 @@ package de.bluecolored.bluemap.core.world.mca; import com.google.gson.reflect.TypeToken; import de.bluecolored.bluemap.core.util.Key; import de.bluecolored.bluemap.core.world.BlockState; +import de.bluecolored.bluemap.core.world.block.entity.BlockEntity; import de.bluecolored.bluemap.core.world.mca.data.BlockStateDeserializer; import de.bluecolored.bluemap.core.world.mca.data.KeyDeserializer; import de.bluecolored.bluenbt.BlueNBT; @@ -37,6 +38,7 @@ public class MCAUtil { static { BLUENBT.register(TypeToken.get(BlockState.class), new BlockStateDeserializer()); BLUENBT.register(TypeToken.get(Key.class), new KeyDeserializer()); + BLUENBT.register(TypeToken.get(BlockEntity.class), new BlockEntity.BlockEntityDeserializer()); } /** diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java index c0268890..979c1264 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_13.java @@ -30,12 +30,17 @@ import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.DimensionType; import de.bluecolored.bluemap.core.world.LightData; +import de.bluecolored.bluemap.core.world.block.entity.BlockEntity; import de.bluecolored.bluemap.core.world.mca.MCAUtil; import de.bluecolored.bluemap.core.world.mca.MCAWorld; import de.bluecolored.bluenbt.NBTName; import lombok.Getter; import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + public class Chunk_1_13 extends MCAChunk { private static final Key STATUS_EMPTY = new Key("minecraft", "empty"); @@ -58,6 +63,7 @@ public class Chunk_1_13 extends MCAChunk { private final int sectionMin, sectionMax; final int[] biomes; + private final Map blockEntities; public Chunk_1_13(MCAWorld world, Data data) { super(world, data); @@ -113,6 +119,10 @@ public class Chunk_1_13 extends MCAChunk { this.sectionMin = 0; this.sectionMax = 0; } + + this.blockEntities = level.blockEntities.stream().collect(Collectors.toMap( + it -> (long) it.getY() << 8 | (it.getX() & 0xF) << 4 | it.getZ() & 0xF, it -> it + )); } @Override @@ -195,6 +205,11 @@ public class Chunk_1_13 extends MCAChunk { ); } + @Override + public @Nullable BlockEntity getBlockEntity(int x, int y, int z) { + return blockEntities.get((long) y << 8 | (x & 0xF) << 4 | z & 0xF); + } + private @Nullable Section getSection(int y) { y -= sectionMin; if (y < 0 || y >= this.sections.length) return null; @@ -273,6 +288,7 @@ public class Chunk_1_13 extends MCAChunk { private HeightmapsData heightmaps = new HeightmapsData(); private SectionData @Nullable [] sections = null; private int[] biomes = EMPTY_INT_ARRAY; + @NBTName("TileEntities") private List blockEntities = List.of(); } @Getter diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_16.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_16.java index ed9f336f..648c338d 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_16.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_16.java @@ -30,6 +30,7 @@ import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.DimensionType; import de.bluecolored.bluemap.core.world.LightData; +import de.bluecolored.bluemap.core.world.block.entity.BlockEntity; import de.bluecolored.bluemap.core.world.mca.MCAUtil; import de.bluecolored.bluemap.core.world.mca.MCAWorld; import de.bluecolored.bluemap.core.world.mca.PackedIntArrayAccess; @@ -37,6 +38,10 @@ import de.bluecolored.bluenbt.NBTName; import lombok.Getter; import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + public class Chunk_1_16 extends MCAChunk { private static final Key STATUS_EMPTY = new Key("minecraft", "empty"); @@ -57,6 +62,7 @@ public class Chunk_1_16 extends MCAChunk { private final int sectionMin, sectionMax; private final int[] biomes; + private final Map blockEntities; public Chunk_1_16(MCAWorld world, Data data) { super(world, data); @@ -112,6 +118,10 @@ public class Chunk_1_16 extends MCAChunk { this.sectionMin = 0; this.sectionMax = 0; } + + this.blockEntities = level.blockEntities.stream().collect(Collectors.toMap( + it -> (long) it.getY() << 8 | (it.getX() & 0xF) << 4 | it.getZ() & 0xF, it -> it + )); } @Override @@ -191,6 +201,11 @@ public class Chunk_1_16 extends MCAChunk { return oceanFloorHeights.get((z & 0xF) << 4 | x & 0xF); } + @Override + public @Nullable BlockEntity getBlockEntity(int x, int y, int z) { + return blockEntities.get((long) y << 8 | (x & 0xF) << 4 | z & 0xF); + } + private @Nullable Section getSection(int y) { y -= sectionMin; if (y < 0 || y >= this.sections.length) return null; @@ -261,6 +276,7 @@ public class Chunk_1_16 extends MCAChunk { private HeightmapsData heightmaps = new HeightmapsData(); private SectionData @Nullable [] sections = null; private int[] biomes = EMPTY_INT_ARRAY; + @NBTName("TileEntities") private List blockEntities = List.of(); } @Getter diff --git a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_18.java b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_18.java index 3c91351f..9e65f541 100644 --- a/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_18.java +++ b/BlueMapCore/src/main/java/de/bluecolored/bluemap/core/world/mca/chunk/Chunk_1_18.java @@ -30,6 +30,7 @@ import de.bluecolored.bluemap.core.world.Biome; import de.bluecolored.bluemap.core.world.BlockState; import de.bluecolored.bluemap.core.world.DimensionType; import de.bluecolored.bluemap.core.world.LightData; +import de.bluecolored.bluemap.core.world.block.entity.BlockEntity; import de.bluecolored.bluemap.core.world.mca.MCAUtil; import de.bluecolored.bluemap.core.world.mca.MCAWorld; import de.bluecolored.bluemap.core.world.mca.PackedIntArrayAccess; @@ -37,6 +38,10 @@ import de.bluecolored.bluenbt.NBTName; import lombok.Getter; import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + public class Chunk_1_18 extends MCAChunk { private static final Key STATUS_EMPTY = new Key("minecraft", "empty"); @@ -57,6 +62,8 @@ public class Chunk_1_18 extends MCAChunk { private final Section[] sections; private final int sectionMin, sectionMax; + private final Map blockEntities; + public Chunk_1_18(MCAWorld world, Data data) { super(world, data); @@ -108,6 +115,10 @@ public class Chunk_1_18 extends MCAChunk { this.sectionMin = 0; this.sectionMax = 0; } + + this.blockEntities = data.blockEntities.stream().collect(Collectors.toMap( + it -> (long) it.getY() << 8 | (it.getX() & 0xF) << 4 | it.getZ() & 0xF, it -> it + )); } @Override @@ -182,6 +193,11 @@ public class Chunk_1_18 extends MCAChunk { return oceanFloorHeights.get((z & 0xF) << 4 | x & 0xF) + worldMinY; } + @Override + public @Nullable BlockEntity getBlockEntity(int x, int y, int z) { + return blockEntities.get((long) y << 8 | (x & 0xF) << 4 | z & 0xF); + } + private @Nullable Section getSection(int y) { y -= sectionMin; if (y < 0 || y >= this.sections.length) return null; @@ -263,6 +279,7 @@ public class Chunk_1_18 extends MCAChunk { private long inhabitedTime = 0; private HeightmapsData heightmaps = new HeightmapsData(); private SectionData @Nullable [] sections = null; + @NBTName("block_entities") private List blockEntities = List.of(); } @Getter