From 9c77ab267e3ea61dccd8e1d88c5cf30508b0213d Mon Sep 17 00:00:00 2001 From: TheMode Date: Mon, 14 Jun 2021 13:31:14 +0200 Subject: [PATCH] Rework Block implementation (`BlockTest`) --- .../minestom/server/instance/block/Block.java | 55 ++++++---- .../server/instance/block/BlockImpl.java | 51 +-------- .../server/instance/block/BlockRegistry.java | 103 +++++++++++++++--- .../server/instance/block/BlockTest.java | 97 +++++++++++++++++ .../minestom/server/registry/Registry.java | 57 +++------- 5 files changed, 240 insertions(+), 123 deletions(-) create mode 100644 src/main/java/net/minestom/server/instance/block/BlockTest.java diff --git a/src/main/java/net/minestom/server/instance/block/Block.java b/src/main/java/net/minestom/server/instance/block/Block.java index a10dcc363..70567758c 100644 --- a/src/main/java/net/minestom/server/instance/block/Block.java +++ b/src/main/java/net/minestom/server/instance/block/Block.java @@ -22,32 +22,56 @@ import java.util.function.BiPredicate; */ public interface Block extends ProtocolObject, TagReadable, BlockConstants { - @NotNull Block withProperty(@NotNull BlockProperty property, @NotNull T value); - @NotNull Block withProperty(@NotNull String property, @NotNull String value); + default @NotNull Block withProperty(@NotNull BlockProperty property, @NotNull T value) { + return withProperty(property.getName(), value.toString()); + } + @NotNull Block withTag(@NotNull Tag tag, @Nullable T value); @NotNull Block withNbt(@Nullable NBTCompound compound); @NotNull Block withHandler(@Nullable BlockHandler handler); - @NotNull T getProperty(@NotNull BlockProperty property); - @NotNull String getProperty(@NotNull String property); + default @NotNull String getProperty(@NotNull BlockProperty property) { + return getProperty(property.getName()); + } + @Nullable NBTCompound getNbt(); @Nullable BlockHandler getHandler(); - @NotNull Block getDefaultBlock(); - @NotNull Map createPropertiesMap(); - short getStateId(); + @NotNull Registry.BlockEntry registry(); - default @NotNull Registry.BlockEntry registry() { - return Registry.block(this); + @Override + default @NotNull NamespaceID getNamespaceId() { + return NamespaceID.from(registry().namespace()); + } + + @Override + default int getId() { + return registry().id(); + } + + default short getStateId() { + return (short) registry().stateId(); + } + + default boolean isAir() { + return registry().isAir(); + } + + default boolean isSolid() { + return registry().isSolid(); + } + + default boolean isLiquid() { + return registry().isLiquid(); } default boolean compare(@NotNull Block block, @NotNull Comparator comparator) { @@ -80,19 +104,6 @@ public interface Block extends ProtocolObject, TagReadable, BlockConstants { BlockRegistry.register(namespaceID, block, range, blockSupplier); } - default boolean isAir() { - return registry().isAir(); - } - - default boolean isSolid() { - return registry().isSolid(); - } - - default boolean isLiquid() { - return registry().isLiquid(); - } - - @FunctionalInterface interface Comparator extends BiPredicate { Comparator IDENTITY = (b1, b2) -> b1 == b2; diff --git a/src/main/java/net/minestom/server/instance/block/BlockImpl.java b/src/main/java/net/minestom/server/instance/block/BlockImpl.java index 534643e19..85d835c0b 100644 --- a/src/main/java/net/minestom/server/instance/block/BlockImpl.java +++ b/src/main/java/net/minestom/server/instance/block/BlockImpl.java @@ -1,5 +1,6 @@ package net.minestom.server.instance.block; +import net.minestom.server.registry.Registry; import net.minestom.server.tag.Tag; import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.math.IntRange; @@ -9,6 +10,7 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.*; +@Deprecated class BlockImpl implements Block { private NamespaceID namespaceID; @@ -45,36 +47,6 @@ class BlockImpl implements Block { this(namespaceID, blockId, minStateId, stateId, properties, propertiesMap, null); } - @Override - public @NotNull Block withProperty(@NotNull BlockProperty property, @NotNull T value) { - if (properties.isEmpty()) { - // This block doesn't have any state - return this; - } - final int index = properties.indexOf(property); - if (index == -1) { - // Invalid state - return this; - } - - // Find properties map - LinkedHashMap, Object> map; - if (propertiesMap == null) { - // Represents the first id, create a new map - map = new LinkedHashMap<>(); - properties.forEach(prop -> map.put(prop, prop.equals(property) ? value : null)); - } else { - // Change property - map = (LinkedHashMap, Object>) propertiesMap.clone(); - map.put(property, value); - } - - var block = shallowClone(); - block.stateId = computeId(minStateId, properties, map); - block.propertiesMap = map; - return block; - } - @Override public @NotNull Block withProperty(@NotNull String property, @NotNull String value) { // TODO @@ -121,11 +93,6 @@ class BlockImpl implements Block { return block; } - @Override - public @NotNull T getProperty(@NotNull BlockProperty property) { - return (T) propertiesMap.get(property); - } - @Override public @NotNull String getProperty(@NotNull String property) { // TODO @@ -137,11 +104,6 @@ class BlockImpl implements Block { return compound != null ? compound.deepClone() : null; } - @Override - public @NotNull Block getDefaultBlock() { - return original; - } - @Override public @NotNull NamespaceID getNamespaceId() { return namespaceID; @@ -155,13 +117,8 @@ class BlockImpl implements Block { } @Override - public int getId() { - return blockId; - } - - @Override - public short getStateId() { - return stateId; + public @NotNull Registry.BlockEntry registry() { + return BlockRegistry.getState(stateId).registry(); // TODO } @Override diff --git a/src/main/java/net/minestom/server/instance/block/BlockRegistry.java b/src/main/java/net/minestom/server/instance/block/BlockRegistry.java index 2cd92cde5..a5e2f395c 100644 --- a/src/main/java/net/minestom/server/instance/block/BlockRegistry.java +++ b/src/main/java/net/minestom/server/instance/block/BlockRegistry.java @@ -23,20 +23,35 @@ class BlockRegistry { // Property name -> values private static final Map> PROPERTIES_MAP = new ConcurrentHashMap<>(); + // Unique name -> IG key + private static final Map PROPERTIES_NAME_MAP = new ConcurrentHashMap<>(); + // Block namespace -> registry data - private static final Map BLOCK_MAP = new ConcurrentHashMap<>(); + private static final Map NAMESPACE_MAP = new ConcurrentHashMap<>(); + // Block id -> registry data + private static final Map BLOCK_ID_MAP = new ConcurrentHashMap<>(); + // Block state -> block object + private static final Map BLOCK_STATE_MAP = new ConcurrentHashMap<>(); + // Block namespace -> properties map to block access + private static final Map BLOCK_PROPERTY_MAP = new ConcurrentHashMap<>(); private static final Map namespaceMap = new HashMap<>(); private static final Int2ObjectSortedMap blockSet = new Int2ObjectAVLTreeMap<>(); private static final Short2ObjectSortedMap stateSet = new Short2ObjectAVLTreeMap<>(); + private static class PropertyEntry { + private final Map, Block> propertyMap = new ConcurrentHashMap<>(); + } + static { // Load data from file // Block properties JsonObject properties = Registry.load(Registry.Resource.BLOCK_PROPERTY); - properties.keySet().forEach(propertyName -> { - final JsonObject propertyObject = properties.getAsJsonObject(propertyName); + properties.entrySet().forEach(entry -> { + final String propertyName = entry.getKey(); + final JsonObject propertyObject = entry.getValue().getAsJsonObject(); + final String key = propertyObject.get("key").getAsString(); final JsonArray values = propertyObject.getAsJsonArray("values"); @@ -44,23 +59,85 @@ class BlockRegistry { values.forEach(jsonElement -> stringValues.add(jsonElement.toString())); PROPERTIES_MAP.put(key, stringValues); + PROPERTIES_NAME_MAP.put(propertyName, key); }); // Blocks JsonObject blocks = Registry.load(Registry.Resource.BLOCK); - blocks.keySet().forEach(blockNamespace -> { - final JsonObject blockObject = properties.getAsJsonObject(blockNamespace); - BLOCK_MAP.put(blockNamespace, blockObject); - final JsonObject propertiesObject = blockObject.getAsJsonObject("properties"); - final JsonArray statesObject = blockObject.getAsJsonArray("states"); - { - // To do not be cloned over and over - blockObject.remove("properties"); - blockObject.remove("states"); - } + blocks.entrySet().forEach(entry -> { + final String blockNamespace = entry.getKey(); + final JsonObject blockObject = entry.getValue().getAsJsonObject(); + + retrieveState(blockNamespace, blockObject); + final int defaultState = blockObject.get("defaultStateId").getAsInt(); + final Block defaultBlock = getState(defaultState); + final int id = blockObject.get("id").getAsInt(); + BLOCK_ID_MAP.put(id, defaultBlock); + NAMESPACE_MAP.put(blockNamespace, defaultBlock); }); } + private static void retrieveState(String namespace, JsonObject object) { + final JsonObject states = object.getAsJsonObject("states"); + + PropertyEntry propertyEntry = new PropertyEntry(); + states.entrySet().forEach(stateEntry -> { + final String query = stateEntry.getKey(); + + JsonObject stateObject = object.deepCopy(); + + stateObject.remove("states"); + stateObject.remove("properties"); + + JsonObject stateOverride = stateEntry.getValue().getAsJsonObject(); + stateOverride.entrySet().forEach(entry -> stateObject.add(entry.getKey(), entry.getValue())); + final int stateId = stateOverride.get("stateId").getAsInt(); + + final var propertyMap = getPropertyMap(query); + final Block block = new BlockTest(stateObject); + BLOCK_STATE_MAP.put(stateId, block); + propertyEntry.propertyMap.put(propertyMap, block); + }); + BLOCK_PROPERTY_MAP.put(namespace, propertyEntry); + } + + private static Map getPropertyMap(String query) { + Map result = new HashMap<>(); + final String propertiesString = query.substring(1, query.length() - 1); + StringBuilder keyBuilder = new StringBuilder(); + StringBuilder valueBuilder = new StringBuilder(); + StringBuilder builder = keyBuilder; + for (int i = 0; i < propertiesString.length(); i++) { + final char c = propertiesString.charAt(i); + if (c == '=') { + // Switch to value builder + builder = valueBuilder; + } else if (c == ',') { + // Append current text + result.put(keyBuilder.toString(), valueBuilder.toString()); + keyBuilder = new StringBuilder(); + valueBuilder = new StringBuilder(); + builder = keyBuilder; + } else if (c != ' ') { + builder.append(c); + } + } + return result; + } + + public static synchronized @Nullable Block get(@NotNull String namespace) { + return NAMESPACE_MAP.get(namespace); + } + + public static @Nullable Block getState(int stateId) { + return BLOCK_STATE_MAP.get(stateId); + } + + public static @Nullable Block getProperties(String namespace, Map properties) { + final var entry = BLOCK_PROPERTY_MAP.get(namespace); + return entry.propertyMap.get(properties); + } + public static synchronized @Nullable Block fromNamespaceId(@NotNull NamespaceID namespaceID) { return namespaceMap.get(namespaceID); } diff --git a/src/main/java/net/minestom/server/instance/block/BlockTest.java b/src/main/java/net/minestom/server/instance/block/BlockTest.java new file mode 100644 index 000000000..01bf965d3 --- /dev/null +++ b/src/main/java/net/minestom/server/instance/block/BlockTest.java @@ -0,0 +1,97 @@ +package net.minestom.server.instance.block; + +import com.google.gson.JsonObject; +import net.minestom.server.registry.Registry; +import net.minestom.server.tag.Tag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +class BlockTest implements Block { + + private final Registry.BlockEntry registry; + + private final Map properties = new HashMap<>(); + private NBTCompound compound; + private BlockHandler handler; + + BlockTest(Registry.BlockEntry registry) { + this.registry = registry; + } + + BlockTest(JsonObject jsonObject) { + this(Registry.block(jsonObject)); + } + + @Override + public @NotNull Block withProperty(@NotNull String property, @NotNull String value) { + var properties = new HashMap<>(this.properties); + properties.put(property, value); + return Objects.requireNonNull(BlockRegistry.getProperties(getNamespaceId().asString(), properties)); + } + + @Override + public @NotNull Block withTag(@NotNull Tag tag, @Nullable T value) { + var clone = shallowClone(); + clone.compound = Objects.requireNonNullElseGet(clone.compound, NBTCompound::new); + tag.write(clone.compound, value); + return clone; + } + + @Override + public @NotNull Block withNbt(@Nullable NBTCompound compound) { + var clone = shallowClone(); + clone.compound = compound; + return clone; + } + + @Override + public @NotNull Block withHandler(@Nullable BlockHandler handler) { + var clone = shallowClone(); + clone.handler = handler; + return clone; + } + + @Override + public @NotNull String getProperty(@NotNull String property) { + return properties.get(property); + } + + @Override + public @Nullable NBTCompound getNbt() { + return compound.deepClone(); + } + + @Override + public @Nullable BlockHandler getHandler() { + return handler; + } + + @Override + public @NotNull Map createPropertiesMap() { + return new HashMap<>(properties); + } + + @Override + public @NotNull Registry.BlockEntry registry() { + return registry; + } + + @Override + public @Nullable T getTag(@NotNull Tag tag) { + return tag.read(compound); + } + + @Override + public boolean hasTag(@NotNull Tag tag) { + return compound.containsKey(tag.getKey()); + } + + private @NotNull BlockTest shallowClone() { + return new BlockTest(registry); + } +} diff --git a/src/main/java/net/minestom/server/registry/Registry.java b/src/main/java/net/minestom/server/registry/Registry.java index bc9f6ef96..f70babfd0 100644 --- a/src/main/java/net/minestom/server/registry/Registry.java +++ b/src/main/java/net/minestom/server/registry/Registry.java @@ -4,32 +4,22 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import net.minestom.server.MinecraftServer; -import net.minestom.server.instance.block.Block; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import java.io.InputStreamReader; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; +@ApiStatus.Internal public class Registry { - private static final Loader LOADER = new Loader(); protected static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); - public static BlockEntry block(@NotNull Block block) { - return loader().block(block.getName()); + public static BlockEntry block(@NotNull JsonObject jsonObject) { + return new BlockEntry(jsonObject); } - @ApiStatus.Internal - public static @NotNull Loader loader() { - return LOADER; - } - - @ApiStatus.Internal public static JsonObject load(Resource resource) { - final String path = String.format("/%s_%s.json", MinecraftServer.VERSION_NAME_UNDERSCORED, resource.name); + final String path = String.format("/%s.json", resource.name); final var resourceStream = Registry.class.getResourceAsStream(path); return GSON.fromJson(new InputStreamReader(resourceStream), JsonObject.class); } @@ -50,6 +40,18 @@ public class Registry { super(json); } + public String namespace() { + return getString("namespace"); + } + + public int id() { + return getInt("id"); + } + + public int stateId() { + return getInt("stateId"); + } + public float destroySpeed() { return getFloat("destroySpeed"); } @@ -110,31 +112,4 @@ public class Registry { return json.get(name); } } - - public static class Loader { - private final RegistryMap blockRegistry = new RegistryMap<>(BlockEntry::new); - - public void loadBlocks(@NotNull JsonObject blocks) { - loadRegistry(blockRegistry, blocks); - } - - public BlockEntry block(String name) { - return blockRegistry.get(name); - } - - private void loadRegistry(RegistryMap map, JsonObject data) { - data.keySet().forEach(namespace -> { - final JsonObject value = data.get(namespace).getAsJsonObject(); - map.put(namespace, map.function.apply(value)); - }); - } - } - - private static class RegistryMap extends ConcurrentHashMap { - private final Function function; - - private RegistryMap(Function function) { - this.function = function; - } - } }