mirror of
https://github.com/Minestom/Minestom.git
synced 2024-11-08 11:50:36 +01:00
Rework Block implementation (BlockTest
)
This commit is contained in:
parent
adba6c3d40
commit
9c77ab267e
@ -22,32 +22,56 @@ import java.util.function.BiPredicate;
|
||||
*/
|
||||
public interface Block extends ProtocolObject, TagReadable, BlockConstants {
|
||||
|
||||
<T> @NotNull Block withProperty(@NotNull BlockProperty<T> property, @NotNull T value);
|
||||
|
||||
@NotNull Block withProperty(@NotNull String property, @NotNull String value);
|
||||
|
||||
default <T> @NotNull Block withProperty(@NotNull BlockProperty<T> property, @NotNull T value) {
|
||||
return withProperty(property.getName(), value.toString());
|
||||
}
|
||||
|
||||
<T> @NotNull Block withTag(@NotNull Tag<T> tag, @Nullable T value);
|
||||
|
||||
@NotNull Block withNbt(@Nullable NBTCompound compound);
|
||||
|
||||
@NotNull Block withHandler(@Nullable BlockHandler handler);
|
||||
|
||||
<T> @NotNull T getProperty(@NotNull BlockProperty<T> property);
|
||||
|
||||
@NotNull String getProperty(@NotNull String property);
|
||||
|
||||
default <T> @NotNull String getProperty(@NotNull BlockProperty<T> property) {
|
||||
return getProperty(property.getName());
|
||||
}
|
||||
|
||||
@Nullable NBTCompound getNbt();
|
||||
|
||||
@Nullable BlockHandler getHandler();
|
||||
|
||||
@NotNull Block getDefaultBlock();
|
||||
|
||||
@NotNull Map<String, String> 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<Block, Block> {
|
||||
Comparator IDENTITY = (b1, b2) -> b1 == b2;
|
||||
|
@ -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 <T> Block withProperty(@NotNull BlockProperty<T> 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<BlockProperty<?>, 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<BlockProperty<?>, 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 <T> @NotNull T getProperty(@NotNull BlockProperty<T> 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
|
||||
|
@ -23,20 +23,35 @@ class BlockRegistry {
|
||||
|
||||
// Property name -> values
|
||||
private static final Map<String, List<String>> PROPERTIES_MAP = new ConcurrentHashMap<>();
|
||||
// Unique name -> IG key
|
||||
private static final Map<String, String> PROPERTIES_NAME_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
// Block namespace -> registry data
|
||||
private static final Map<String, JsonObject> BLOCK_MAP = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Block> NAMESPACE_MAP = new ConcurrentHashMap<>();
|
||||
// Block id -> registry data
|
||||
private static final Map<Integer, Block> BLOCK_ID_MAP = new ConcurrentHashMap<>();
|
||||
// Block state -> block object
|
||||
private static final Map<Integer, Block> BLOCK_STATE_MAP = new ConcurrentHashMap<>();
|
||||
// Block namespace -> properties map to block access
|
||||
private static final Map<String, PropertyEntry> BLOCK_PROPERTY_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Map<NamespaceID, Block> namespaceMap = new HashMap<>();
|
||||
private static final Int2ObjectSortedMap<Block> blockSet = new Int2ObjectAVLTreeMap<>();
|
||||
private static final Short2ObjectSortedMap<Block.Supplier> stateSet = new Short2ObjectAVLTreeMap<>();
|
||||
|
||||
private static class PropertyEntry {
|
||||
private final Map<Map<String, String>, 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<String, String> getPropertyMap(String query) {
|
||||
Map<String, String> 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<String, String> 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);
|
||||
}
|
||||
|
@ -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<String, String> 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 <T> Block withTag(@NotNull Tag<T> 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<String, String> createPropertiesMap() {
|
||||
return new HashMap<>(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Registry.BlockEntry registry() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @Nullable T getTag(@NotNull Tag<T> 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);
|
||||
}
|
||||
}
|
@ -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<BlockEntry> blockRegistry = new RegistryMap<>(BlockEntry::new);
|
||||
|
||||
public void loadBlocks(@NotNull JsonObject blocks) {
|
||||
loadRegistry(blockRegistry, blocks);
|
||||
}
|
||||
|
||||
public BlockEntry block(String name) {
|
||||
return blockRegistry.get(name);
|
||||
}
|
||||
|
||||
private <T extends Entry> void loadRegistry(RegistryMap<T> 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<T extends Entry> extends ConcurrentHashMap<String, T> {
|
||||
private final Function<JsonObject, T> function;
|
||||
|
||||
private RegistryMap(Function<JsonObject, T> function) {
|
||||
this.function = function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user