Implement a Registry for BlockEntities

This commit is contained in:
Lukas Rieger (Blue) 2024-05-28 01:57:35 +02:00
parent 02d9fc1405
commit b1c75aa44a
No known key found for this signature in database
GPG Key ID: AA33883B1BBA03E6
8 changed files with 116 additions and 108 deletions

View File

@ -25,113 +25,58 @@
package de.bluecolored.bluemap.core.world.block.entity;
import com.google.gson.reflect.TypeToken;
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluenbt.BlueNBT;
import de.bluecolored.bluenbt.NBTDeserializer;
import de.bluecolored.bluenbt.NBTReader;
import de.bluecolored.bluenbt.TypeDeserializer;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.jetbrains.annotations.Nullable;
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;
@Getter
@EqualsAndHashCode
@ToString
@NBTDeserializer(BlockEntity.BlockEntityDeserializer.class)
public class BlockEntity {
private static final BlueNBT BLUENBT = new BlueNBT();
@FunctionalInterface
private interface BlockEntityInitializer {
BlockEntity create(Map<String, Object> data);
}
@SuppressWarnings("StaticInitializerReferencesSubClass")
private static final Map<String, BlockEntityInitializer> ID_MAPPING = Map.of(
"minecraft:sign", SignBlockEntity::new,
"minecraft:skull", SkullBlockEntity::new,
"minecraft:banner", BannerBlockEntity::new
);
public abstract class BlockEntity {
protected final String id;
protected final int x, y, z;
protected final boolean keepPacked;
protected BlockEntity(Map<String, Object> 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 +
'}';
protected BlockEntity(Map<String, Object> raw) {
this.id = (String) raw.get("id");
this.x = (int) raw.getOrDefault("x", 0);
this.y = (int) raw.getOrDefault("y", 0);
this.z = (int) raw.getOrDefault("z", 0);
this.keepPacked = (byte) raw.getOrDefault("keepPacked", (byte) 0) == 1;
}
@RequiredArgsConstructor
public static class BlockEntityDeserializer implements TypeDeserializer<BlockEntity> {
private final BlueNBT blueNBT;
@Override
public BlockEntity read(NBTReader reader) throws IOException {
@SuppressWarnings("unchecked") Map<String, Object> data =
(Map<String, Object>) BLUENBT.read(reader, TypeToken.getParameterized(Map.class, String.class, Object.class));
@SuppressWarnings("unchecked")
public @Nullable BlockEntity read(NBTReader reader) throws IOException {
Map<String, Object> raw = (Map<String, Object>) blueNBT.read(reader, TypeToken.getParameterized(Map.class, String.class, Object.class));
String id = (String) data.get("id");
if (id == null || id.isBlank()) {
return null;
}
String id = (String) raw.get("id");
if (id == null) return null;
BlockEntityInitializer instance = ID_MAPPING.getOrDefault(id, BlockEntity::new);
Key typeKey = Key.parse(id, Key.MINECRAFT_NAMESPACE);
BlockEntityType type = BlockEntityType.REGISTRY.get(typeKey);
if (type == null) return null;
try {
return instance.create(data);
} catch (Exception e) {
Logger.global.logError("Failed to instantiate BlockEntity instance!", e);
}
return null;
return type.load(raw);
}
}
}

View File

@ -0,0 +1,9 @@
package de.bluecolored.bluemap.core.world.block.entity;
import java.util.Map;
public interface BlockEntityLoader {
BlockEntity load(Map<String, Object> raw);
}

View File

@ -0,0 +1,37 @@
package de.bluecolored.bluemap.core.world.block.entity;
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluemap.core.util.Keyed;
import de.bluecolored.bluemap.core.util.Registry;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Map;
public interface BlockEntityType extends Keyed, BlockEntityLoader {
BlockEntityType SIGN = new Impl(Key.minecraft("sign"), SignBlockEntity::new);
BlockEntityType SKULL = new Impl(Key.minecraft("skull"), SkullBlockEntity::new);
BlockEntityType BANNER = new Impl(Key.minecraft("banner"), BannerBlockEntity::new);
Registry<BlockEntityType> REGISTRY = new Registry<>(
SIGN,
SKULL,
BANNER
);
@RequiredArgsConstructor
class Impl implements BlockEntityType {
@Getter
private final Key key;
private final BlockEntityLoader loader;
@Override
public BlockEntity load(Map<String, Object> raw) {
return loader.load(raw);
}
}
}

View File

@ -41,7 +41,7 @@ public class MCAUtil {
public static BlueNBT addCommonNbtAdapters(BlueNBT nbt) {
nbt.register(TypeToken.get(BlockState.class), new BlockStateDeserializer());
nbt.register(TypeToken.get(Key.class), new KeyDeserializer());
nbt.register(TypeToken.get(BlockEntity.class), new BlockEntity.BlockEntityDeserializer());
nbt.register(TypeToken.get(BlockEntity.class), new BlockEntity.BlockEntityDeserializer(nbt));
return nbt;
}

View File

@ -26,10 +26,10 @@
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluemap.core.world.biome.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.biome.Biome;
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;
@ -37,9 +37,8 @@
import lombok.Getter;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class Chunk_1_13 extends MCAChunk {
@ -120,9 +119,15 @@ public Chunk_1_13(MCAWorld world, Data data) {
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
));
// load block-entities
this.blockEntities = new HashMap<>();
for (int i = 0; i < level.blockEntities.length; i++) {
BlockEntity be = level.blockEntities[i];
if (be == null) continue;
long hash = (long) be.getY() << 8 | (be.getX() & 0xF) << 4 | be.getZ() & 0xF;
blockEntities.put(hash, be);
}
}
@Override
@ -290,7 +295,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<BlockEntity> blockEntities = List.of();
@NBTName("TileEntities") private @Nullable BlockEntity [] blockEntities = EMPTY_BLOCK_ENTITIES_ARRAY;
}
@Getter

View File

@ -26,10 +26,10 @@
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluemap.core.world.biome.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.biome.Biome;
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;
@ -38,9 +38,8 @@
import lombok.Getter;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class Chunk_1_16 extends MCAChunk {
@ -119,9 +118,15 @@ public Chunk_1_16(MCAWorld world, Data data) {
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
));
// load block-entities
this.blockEntities = new HashMap<>();
for (int i = 0; i < level.blockEntities.length; i++) {
BlockEntity be = level.blockEntities[i];
if (be == null) continue;
long hash = (long) be.getY() << 8 | (be.getX() & 0xF) << 4 | be.getZ() & 0xF;
blockEntities.put(hash, be);
}
}
@Override
@ -277,7 +282,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<BlockEntity> blockEntities = List.of();
@NBTName("TileEntities") private @Nullable BlockEntity [] blockEntities = EMPTY_BLOCK_ENTITIES_ARRAY;
}
@Getter

View File

@ -26,10 +26,10 @@
import de.bluecolored.bluemap.core.logger.Logger;
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluemap.core.world.biome.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.biome.Biome;
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;
@ -38,9 +38,8 @@
import lombok.Getter;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class Chunk_1_18 extends MCAChunk {
@ -116,9 +115,15 @@ public Chunk_1_18(MCAWorld world, Data data) {
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
));
// load block-entities
this.blockEntities = new HashMap<>();
for (int i = 0; i < data.blockEntities.length; i++) {
BlockEntity be = data.blockEntities[i];
if (be == null) continue;
long hash = (long) be.getY() << 8 | (be.getX() & 0xF) << 4 | be.getZ() & 0xF;
blockEntities.put(hash, be);
}
}
@Override
@ -285,7 +290,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<BlockEntity> blockEntities = List.of();
@NBTName("block_entities") private @Nullable BlockEntity [] blockEntities = EMPTY_BLOCK_ENTITIES_ARRAY;
}
@Getter

View File

@ -27,6 +27,7 @@
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluemap.core.world.BlockState;
import de.bluecolored.bluemap.core.world.Chunk;
import de.bluecolored.bluemap.core.world.block.entity.BlockEntity;
import de.bluecolored.bluemap.core.world.mca.MCAWorld;
import lombok.Getter;
import lombok.ToString;
@ -44,6 +45,7 @@ public abstract class MCAChunk implements Chunk {
protected static final long[] EMPTY_LONG_ARRAY = new long[0];
protected static final Key[] EMPTY_KEY_ARRAY = new Key[0];
protected static final BlockState[] EMPTY_BLOCKSTATE_ARRAY = new BlockState[0];
protected static final BlockEntity[] EMPTY_BLOCK_ENTITIES_ARRAY = new BlockEntity[0];
private final MCAWorld world;
private final int dataVersion;