Skip to content

Commit

Permalink
Add mechanism for retrieving BlockEntity data
Browse files Browse the repository at this point in the history
This commit adds a mechanism for retrieving block entity data.
Block entity data is required to support for example text on signs,
banner patterns, or mods such as Domum Ornamentum.
  • Loading branch information
glorantq committed Mar 17, 2024
1 parent 10fb88d commit e92f106
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
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.BlockEntity;

import java.io.IOException;
import java.nio.file.Path;
Expand Down Expand Up @@ -528,6 +529,8 @@ private Text formatBlock(Block<?> block) {
lines.put("block-light", block.getBlockLightLevel());
lines.put("sun-light", block.getSunLightLevel());

block.getBlockEntity().ifPresent(entity -> lines.put("block-entity-data", entity));

Object[] textElements = lines.entrySet().stream()
.flatMap(e -> Stream.of(TextColor.GRAY, e.getKey(), ": ", TextColor.WHITE, e.getValue(), "\n"))
.toArray(Object[]::new);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
*/
package de.bluecolored.bluemap.core.world;

import de.bluecolored.bluemap.core.world.block.BlockEntity;

import java.util.Optional;

public interface Chunk {

Chunk EMPTY_CHUNK = new Chunk() {};
Expand Down Expand Up @@ -72,4 +76,5 @@ default boolean hasOceanFloorHeights() {

default int getOceanFloorY(int x, int z) { return 0; }

default Optional<BlockEntity> getBlockEntity(int x, int y, int z) { return Optional.empty(); };
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import de.bluecolored.bluemap.core.world.LightData;
import de.bluecolored.bluemap.core.world.World;

import java.util.Optional;

public class Block<T extends Block<T>> {

private World world;
Expand Down Expand Up @@ -147,6 +149,10 @@ public int getBlockLightLevel() {
return getLightData().getBlockLight();
}

public Optional<BlockEntity> getBlockEntity() {
return getChunk().getBlockEntity(x, y, z);
}

@Override
public String toString() {
if (world != null) {
Expand Down Expand Up @@ -174,5 +180,4 @@ public String toString() {
protected T self() {
return (T) this;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package de.bluecolored.bluemap.core.world.block;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class BlockEntity {
private static final List<String> COMMON_KEYS = List.of("id", "x", "y", "z", "keepPacked");

private final String id;
private final int x, y, z;
private final boolean keepPacked;
private final Map<String, Object> data;

public BlockEntity(Map<String, Object> rawData) {
this.id = getString(rawData, "id", "");
this.x = getInt(rawData, "x", 0);
this.y = getInt(rawData, "y", 0);
this.z = getInt(rawData, "z", 0);
this.keepPacked = getBoolean(rawData, "keepPacked", false);

this.data = rawData.entrySet().stream()
.filter(it -> !COMMON_KEYS.contains(it.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

public int getInt(Map<String, Object> tree, String key, int def) {
Object obj = get(tree, key, String.valueOf(def));

try {
return Integer.parseInt(String.valueOf(obj));
} catch (Exception e) {
return def;
}
}

public int getInt(String key, int def) {
return getInt(data, key, def);
}

public boolean getBoolean(Map<String, Object> tree, String key, boolean def) {
Object obj = get(tree, key, String.valueOf(def));

try {
return Boolean.parseBoolean(String.valueOf(obj));
} catch (Exception e) {
return def;
}
}

public boolean getBoolean(String key, boolean def) {
return getBoolean(data, key, def);
}

public String getString(Map<String, Object> tree, String key, String def) {
Object obj = get(tree, key, String.valueOf(def));

try {
return String.valueOf(obj);
} catch (Exception e) {
return def;
}
}

@SuppressWarnings("unchecked")
public Map<String, Object> getSubtree(Map<String, Object> tree, String key) {
Object obj = get(tree, key, Map.of());

if (!(obj instanceof Map)) {
return Map.of();
}

return (Map<String, Object>) obj;
}

public Map<String, Object> getSubtree(String key) {
return getSubtree(data, key);
}

public Object get(Map<String, Object> tree, String key, Object def) {
return tree.getOrDefault(key, def);
}

public Object get(String key, Object def) {
return get(data, key, def);
}

public String getString(String key, String def) {
return getString(data, key, def);
}

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

public Map<String, Object> getData() {
return data;
}

@Override
public String toString() {
return "BlockEntity{" +
"id='" + id + '\'' +
", x=" + x +
", y=" + y +
", z=" + z +
", keepPacked=" + keepPacked +
", data=" + data +
'}';
}

@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) && Objects.equals(data, that.data);
}

@Override
public int hashCode() {
return Objects.hash(id, x, y, z, keepPacked, data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,18 @@
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.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.Optional;
import java.util.stream.Collectors;

public class Chunk_1_13 extends MCAChunk {

private static final Key STATUS_EMPTY = new Key("minecraft", "empty");
Expand All @@ -58,6 +64,7 @@ public class Chunk_1_13 extends MCAChunk {
private final int sectionMin, sectionMax;

final int[] biomes;
private final List<BlockEntity> blockEntities;

public Chunk_1_13(MCAWorld world, Data data) {
super(world, data);
Expand Down Expand Up @@ -113,6 +120,8 @@ public Chunk_1_13(MCAWorld world, Data data) {
this.sectionMin = 0;
this.sectionMax = 0;
}

this.blockEntities = level.blockEntities.stream().map(BlockEntity::new).collect(Collectors.toList());
}

@Override
Expand Down Expand Up @@ -195,6 +204,13 @@ public int getOceanFloorY(int x, int z) {
);
}

@Override
public Optional<BlockEntity> getBlockEntity(int x, int y, int z) {
return blockEntities.stream()
.filter(it -> it.getX() == x && it.getY() == y && it.getZ() == z)
.findFirst();
}

private @Nullable Section getSection(int y) {
y -= sectionMin;
if (y < 0 || y >= this.sections.length) return null;
Expand Down Expand Up @@ -273,6 +289,7 @@ public static class Level {
private HeightmapsData heightmaps = new HeightmapsData();
private SectionData @Nullable [] sections = null;
private int[] biomes = EMPTY_INT_ARRAY;
@NBTName("TileEntities") private List<Map<String, Object>> blockEntities = List.of(Map.of());
}

@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,19 @@
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.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;
import de.bluecolored.bluenbt.NBTName;
import lombok.Getter;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public class Chunk_1_16 extends MCAChunk {

private static final Key STATUS_EMPTY = new Key("minecraft", "empty");
Expand All @@ -57,6 +63,7 @@ public class Chunk_1_16 extends MCAChunk {
private final int sectionMin, sectionMax;

private final int[] biomes;
private final List<BlockEntity> blockEntities;

public Chunk_1_16(MCAWorld world, Data data) {
super(world, data);
Expand Down Expand Up @@ -112,6 +119,8 @@ public Chunk_1_16(MCAWorld world, Data data) {
this.sectionMin = 0;
this.sectionMax = 0;
}

this.blockEntities = level.blockEntities.stream().map(BlockEntity::new).collect(Collectors.toList());
}

@Override
Expand Down Expand Up @@ -191,6 +200,13 @@ public int getOceanFloorY(int x, int z) {
return oceanFloorHeights.get((z & 0xF) << 4 | x & 0xF);
}

@Override
public Optional<BlockEntity> getBlockEntity(int x, int y, int z) {
return blockEntities.stream()
.filter(it -> it.getX() == x && it.getY() == y && it.getZ() == z)
.findFirst();
}

private @Nullable Section getSection(int y) {
y -= sectionMin;
if (y < 0 || y >= this.sections.length) return null;
Expand Down Expand Up @@ -261,6 +277,7 @@ public static class Level {
private HeightmapsData heightmaps = new HeightmapsData();
private SectionData @Nullable [] sections = null;
private int[] biomes = EMPTY_INT_ARRAY;
@NBTName("TileEntities") private List<Map<String, Object>> blockEntities = List.of(Map.of());
}

@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,19 @@
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.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;
import de.bluecolored.bluenbt.NBTName;
import lombok.Getter;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

public class Chunk_1_18 extends MCAChunk {

private static final Key STATUS_EMPTY = new Key("minecraft", "empty");
Expand All @@ -56,6 +62,7 @@ public class Chunk_1_18 extends MCAChunk {

private final Section[] sections;
private final int sectionMin, sectionMax;
private final List<BlockEntity> blockEntities;

public Chunk_1_18(MCAWorld world, Data data) {
super(world, data);
Expand Down Expand Up @@ -108,6 +115,8 @@ public Chunk_1_18(MCAWorld world, Data data) {
this.sectionMin = 0;
this.sectionMax = 0;
}

this.blockEntities = data.blockEntities.stream().map(BlockEntity::new).collect(Collectors.toList());
}

@Override
Expand Down Expand Up @@ -182,6 +191,13 @@ public int getOceanFloorY(int x, int z) {
return oceanFloorHeights.get((z & 0xF) << 4 | x & 0xF) + worldMinY;
}

@Override
public Optional<BlockEntity> getBlockEntity(int x, int y, int z) {
return blockEntities.stream()
.filter(it -> it.getX() == x && it.getY() == y && it.getZ() == z)
.findFirst();
}

private @Nullable Section getSection(int y) {
y -= sectionMin;
if (y < 0 || y >= this.sections.length) return null;
Expand Down Expand Up @@ -263,6 +279,7 @@ public static class Data extends MCAChunk.Data {
private long inhabitedTime = 0;
private HeightmapsData heightmaps = new HeightmapsData();
private SectionData @Nullable [] sections = null;
@NBTName("block_entities") private List<Map<String, Object>> blockEntities = List.of(Map.of());
}

@Getter
Expand Down

0 comments on commit e92f106

Please sign in to comment.