From 0743759eed0c956556f86f5247c5ec11cb181698 Mon Sep 17 00:00:00 2001 From: TheMode Date: Fri, 4 Feb 2022 22:28:05 +0100 Subject: [PATCH] Do not cache registry (#651) --- .../server/entity/EntityTypeImpl.java | 2 +- .../server/instance/block/BlockImpl.java | 30 +- .../minestom/server/item/EnchantmentImpl.java | 2 +- .../minestom/server/item/MaterialImpl.java | 2 +- .../server/particle/ParticleImpl.java | 2 +- .../server/potion/PotionEffectImpl.java | 2 +- .../server/potion/PotionTypeImpl.java | 2 +- .../minestom/server/registry/Registry.java | 439 ++++++++---------- .../minestom/server/sound/SoundEventImpl.java | 2 +- .../server/statistic/StatisticTypeImpl.java | 2 +- .../server/utils/collection/MergedMap.java | 90 ++++ 11 files changed, 318 insertions(+), 257 deletions(-) create mode 100644 src/main/java/net/minestom/server/utils/collection/MergedMap.java diff --git a/src/main/java/net/minestom/server/entity/EntityTypeImpl.java b/src/main/java/net/minestom/server/entity/EntityTypeImpl.java index d2b7e0337..e912ed0bb 100644 --- a/src/main/java/net/minestom/server/entity/EntityTypeImpl.java +++ b/src/main/java/net/minestom/server/entity/EntityTypeImpl.java @@ -44,7 +44,7 @@ import java.util.function.BiFunction; record EntityTypeImpl(Registry.EntityEntry registry) implements EntityType { private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.ENTITIES, - (namespace, object) -> new EntityTypeImpl(Registry.entity(namespace, object, null))); + (namespace, properties) -> new EntityTypeImpl(Registry.entity(namespace, properties))); static final Map> ENTITY_META_SUPPLIER = createMetaMap(); static EntityType get(@NotNull String namespace) { 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 aa816a0fc..66638b242 100644 --- a/src/main/java/net/minestom/server/instance/block/BlockImpl.java +++ b/src/main/java/net/minestom/server/instance/block/BlockImpl.java @@ -9,6 +9,7 @@ import net.minestom.server.tag.Tag; import net.minestom.server.utils.ArrayUtils; import net.minestom.server.utils.ObjectArray; import net.minestom.server.utils.block.BlockUtils; +import net.minestom.server.utils.collection.MergedMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; @@ -20,7 +21,7 @@ import java.util.*; import java.util.function.Function; record BlockImpl(@NotNull Registry.BlockEntry registry, - @NotNull int[] propertiesArray, + int @NotNull [] propertiesArray, @Nullable NBTCompound nbt, @Nullable BlockHandler handler) implements Block { // Block state -> block object @@ -32,20 +33,21 @@ record BlockImpl(@NotNull Registry.BlockEntry registry, // Block id -> Map private static final ObjectArray> POSSIBLE_STATES = new ObjectArray<>(); private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.BLOCKS, - (namespace, object) -> { - final int blockId = ((Number) object.get("id")).intValue(); - final var stateObject = (Map) object.get("states"); + (namespace, properties) -> { + final int blockId = properties.getInt("id"); + final Registry.Properties stateObject = properties.section("states"); // Retrieve properties String[] keys = new String[0]; String[][] values = new String[0][]; { - var properties = (Map) object.get("properties"); - if (properties != null) { - keys = new String[properties.size()]; - values = new String[properties.size()][]; + Registry.Properties stateProperties = properties.section("properties"); + if (stateProperties != null) { + final int stateCount = stateProperties.size(); + keys = new String[stateCount]; + values = new String[stateCount][]; int i = 0; - for (var entry : properties.entrySet()) { + for (var entry : stateProperties) { final int entryIndex = i++; keys[entryIndex] = entry.getKey(); @@ -59,12 +61,11 @@ record BlockImpl(@NotNull Registry.BlockEntry registry, // Retrieve block states { - final var stateEntries = stateObject.entrySet(); - final int propertiesCount = stateEntries.size(); + final int propertiesCount = stateObject.size(); PropertiesHolder[] propertiesKeys = new PropertiesHolder[propertiesCount]; BlockImpl[] blocksValues = new BlockImpl[propertiesCount]; int propertiesOffset = 0; - for (var stateEntry : stateEntries) { + for (var stateEntry : stateObject) { final String query = stateEntry.getKey(); final var stateOverride = (Map) stateEntry.getValue(); final var propertyMap = BlockUtils.parseProperties(query); @@ -82,7 +83,8 @@ record BlockImpl(@NotNull Registry.BlockEntry registry, propertiesArray[keyIndex] = valueIndex; } - final BlockImpl block = new BlockImpl(Registry.block(namespace, object, stateOverride), + var mainProperties = Registry.Properties.fromMap(new MergedMap<>(properties.asMap(), stateOverride)); + final BlockImpl block = new BlockImpl(Registry.block(namespace, mainProperties), propertiesArray, null, null); BLOCK_STATE_MAP.set(block.stateId(), block); propertiesKeys[propertiesOffset] = new PropertiesHolder(propertiesArray); @@ -91,7 +93,7 @@ record BlockImpl(@NotNull Registry.BlockEntry registry, POSSIBLE_STATES.set(blockId, ArrayUtils.toMap(propertiesKeys, blocksValues, propertiesOffset)); } // Register default state - final int defaultState = ((Number) object.get("defaultStateId")).intValue(); + final int defaultState = properties.getInt("defaultStateId"); return getState(defaultState); }); private static final Cache NBT_CACHE = Caffeine.newBuilder() diff --git a/src/main/java/net/minestom/server/item/EnchantmentImpl.java b/src/main/java/net/minestom/server/item/EnchantmentImpl.java index 7e4343bee..b29f07df8 100644 --- a/src/main/java/net/minestom/server/item/EnchantmentImpl.java +++ b/src/main/java/net/minestom/server/item/EnchantmentImpl.java @@ -7,7 +7,7 @@ import java.util.Collection; record EnchantmentImpl(Registry.EnchantmentEntry registry) implements Enchantment { private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.ENCHANTMENTS, - (namespace, object) -> new EnchantmentImpl(Registry.enchantment(namespace, object, null))); + (namespace, properties) -> new EnchantmentImpl(Registry.enchantment(namespace, properties))); static Enchantment get(@NotNull String namespace) { return CONTAINER.get(namespace); diff --git a/src/main/java/net/minestom/server/item/MaterialImpl.java b/src/main/java/net/minestom/server/item/MaterialImpl.java index 873af5b1b..98d371a2a 100644 --- a/src/main/java/net/minestom/server/item/MaterialImpl.java +++ b/src/main/java/net/minestom/server/item/MaterialImpl.java @@ -7,7 +7,7 @@ import java.util.Collection; record MaterialImpl(Registry.MaterialEntry registry) implements Material { private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.ITEMS, - (namespace, object) -> new MaterialImpl(Registry.material(namespace, object, null))); + (namespace, properties) -> new MaterialImpl(Registry.material(namespace, properties))); static Material get(@NotNull String namespace) { return CONTAINER.get(namespace); diff --git a/src/main/java/net/minestom/server/particle/ParticleImpl.java b/src/main/java/net/minestom/server/particle/ParticleImpl.java index 12580ceab..2a41dabfe 100644 --- a/src/main/java/net/minestom/server/particle/ParticleImpl.java +++ b/src/main/java/net/minestom/server/particle/ParticleImpl.java @@ -8,7 +8,7 @@ import java.util.Collection; record ParticleImpl(NamespaceID namespace, int id) implements Particle { private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.PARTICLES, - (namespace, object) -> new ParticleImpl(NamespaceID.from(namespace), ((Number) object.get("id")).intValue())); + (namespace, properties) -> new ParticleImpl(NamespaceID.from(namespace), properties.getInt("id"))); static Particle get(@NotNull String namespace) { return CONTAINER.get(namespace); diff --git a/src/main/java/net/minestom/server/potion/PotionEffectImpl.java b/src/main/java/net/minestom/server/potion/PotionEffectImpl.java index eda988f31..6396afc14 100644 --- a/src/main/java/net/minestom/server/potion/PotionEffectImpl.java +++ b/src/main/java/net/minestom/server/potion/PotionEffectImpl.java @@ -7,7 +7,7 @@ import java.util.Collection; record PotionEffectImpl(Registry.PotionEffectEntry registry) implements PotionEffect { private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.POTION_EFFECTS, - (namespace, object) -> new PotionEffectImpl(Registry.potionEffect(namespace, object, null))); + (namespace, properties) -> new PotionEffectImpl(Registry.potionEffect(namespace, properties))); static PotionEffect get(@NotNull String namespace) { return CONTAINER.get(namespace); diff --git a/src/main/java/net/minestom/server/potion/PotionTypeImpl.java b/src/main/java/net/minestom/server/potion/PotionTypeImpl.java index 1da528935..daf3f1ec7 100644 --- a/src/main/java/net/minestom/server/potion/PotionTypeImpl.java +++ b/src/main/java/net/minestom/server/potion/PotionTypeImpl.java @@ -8,7 +8,7 @@ import java.util.Collection; record PotionTypeImpl(NamespaceID namespace, int id) implements PotionType { private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.POTION_TYPES, - (namespace, object) -> new PotionTypeImpl(NamespaceID.from(namespace), ((Number) object.get("id")).intValue())); + (namespace, properties) -> new PotionTypeImpl(NamespaceID.from(namespace), properties.getInt("id"))); static PotionType get(@NotNull String namespace) { return CONTAINER.get(namespace); diff --git a/src/main/java/net/minestom/server/registry/Registry.java b/src/main/java/net/minestom/server/registry/Registry.java index a10a433e5..39f74d373 100644 --- a/src/main/java/net/minestom/server/registry/Registry.java +++ b/src/main/java/net/minestom/server/registry/Registry.java @@ -26,28 +26,28 @@ import java.util.function.Supplier; */ public final class Registry { @ApiStatus.Internal - public static BlockEntry block(String namespace, @NotNull Map jsonObject, Map override) { - return new BlockEntry(namespace, jsonObject, override); + public static BlockEntry block(String namespace, @NotNull Properties main) { + return new BlockEntry(namespace, main, null); } @ApiStatus.Internal - public static MaterialEntry material(String namespace, @NotNull Map jsonObject, Map override) { - return new MaterialEntry(namespace, jsonObject, override); + public static MaterialEntry material(String namespace, @NotNull Properties main) { + return new MaterialEntry(namespace, main, null); } @ApiStatus.Internal - public static EntityEntry entity(String namespace, @NotNull Map jsonObject, Map override) { - return new EntityEntry(namespace, jsonObject, override); + public static EntityEntry entity(String namespace, @NotNull Properties main) { + return new EntityEntry(namespace, main, null); } @ApiStatus.Internal - public static EnchantmentEntry enchantment(String namespace, @NotNull Map jsonObject, Map override) { - return new EnchantmentEntry(namespace, jsonObject, override); + public static EnchantmentEntry enchantment(String namespace, @NotNull Properties main) { + return new EnchantmentEntry(namespace, main, null); } @ApiStatus.Internal - public static PotionEffectEntry potionEffect(String namespace, @NotNull Map jsonObject, Map override) { - return new PotionEffectEntry(namespace, jsonObject, override); + public static PotionEffectEntry potionEffect(String namespace, @NotNull Properties main) { + return new PotionEffectEntry(namespace, main, null); } @ApiStatus.Internal @@ -73,8 +73,8 @@ public final class Registry { ObjectArray ids = new ObjectArray<>(entries.size()); for (var entry : entries.entrySet()) { final String namespace = entry.getKey(); - final Map object = entry.getValue(); - final T value = loader.get(namespace, object); + final Properties properties = Properties.fromMap(entry.getValue()); + final T value = loader.get(namespace, properties); ids.set(value.id(), value); namespaces.put(value.name(), value); } @@ -119,7 +119,7 @@ public final class Registry { } public interface Loader { - T get(String namespace, Map object); + T get(String namespace, Properties properties); } } @@ -148,7 +148,7 @@ public final class Registry { } } - public static class BlockEntry extends Entry { + public static final class BlockEntry implements Entry { private final NamespaceID namespace; private final int id; private final int stateId; @@ -164,33 +164,34 @@ public final class Registry { private final String blockEntity; private final int blockEntityId; private final Supplier materialSupplier; + private final Properties custom; - private BlockEntry(String namespace, Map main, Map override) { - super(main, override); + private BlockEntry(String namespace, Properties main, Properties custom) { + this.custom = custom; this.namespace = NamespaceID.from(namespace); - this.id = getInt("id"); - this.stateId = getInt("stateId"); - this.translationKey = getString("translationKey"); - this.hardness = getDouble("hardness"); - this.explosionResistance = getDouble("explosionResistance"); - this.friction = getDouble("friction"); - this.speedFactor = getDouble("speedFactor", 1); - this.jumpFactor = getDouble("jumpFactor", 1); - this.air = getBoolean("air", false); - this.solid = getBoolean("solid"); - this.liquid = getBoolean("liquid", false); + this.id = main.getInt("id"); + this.stateId = main.getInt("stateId"); + this.translationKey = main.getString("translationKey"); + this.hardness = main.getDouble("hardness"); + this.explosionResistance = main.getDouble("explosionResistance"); + this.friction = main.getDouble("friction"); + this.speedFactor = main.getDouble("speedFactor", 1); + this.jumpFactor = main.getDouble("jumpFactor", 1); + this.air = main.getBoolean("air", false); + this.solid = main.getBoolean("solid"); + this.liquid = main.getBoolean("liquid", false); { - Map blockEntity = element("blockEntity"); + Properties blockEntity = main.section("blockEntity"); if (blockEntity != null) { - this.blockEntity = (String) blockEntity.get("namespace"); - this.blockEntityId = ((Number) blockEntity.get("id")).intValue(); + this.blockEntity = blockEntity.getString("namespace"); + this.blockEntityId = blockEntity.getInt("id"); } else { this.blockEntity = null; this.blockEntityId = 0; } } { - final String materialNamespace = getString("correspondingItem", null); + final String materialNamespace = main.getString("correspondingItem", null); this.materialSupplier = materialNamespace != null ? () -> Material.fromNamespaceId(materialNamespace) : () -> null; } } @@ -258,9 +259,14 @@ public final class Registry { public @Nullable Material material() { return materialSupplier.get(); } + + @Override + public Properties custom() { + return custom; + } } - public static class MaterialEntry extends Entry { + public static final class MaterialEntry implements Entry { private final NamespaceID namespace; private final int id; private final String translationKey; @@ -269,25 +275,25 @@ public final class Registry { private final boolean isFood; private final Supplier blockSupplier; private final EquipmentSlot equipmentSlot; + private final Properties custom; - private MaterialEntry(String namespace, Map main, Map override) { - super(main, override); + private MaterialEntry(String namespace, Properties main, Properties custom) { + this.custom = custom; this.namespace = NamespaceID.from(namespace); - this.id = getInt("id"); - this.translationKey = getString("translationKey"); - this.maxStackSize = getInt("maxStackSize", 64); - this.maxDamage = getInt("maxDamage", 0); - this.isFood = getBoolean("edible", false); + this.id = main.getInt("id"); + this.translationKey = main.getString("translationKey"); + this.maxStackSize = main.getInt("maxStackSize", 64); + this.maxDamage = main.getInt("maxDamage", 0); + this.isFood = main.getBoolean("edible", false); { - final String blockNamespace = getString("correspondingBlock", null); + final String blockNamespace = main.getString("correspondingBlock", null); this.blockSupplier = blockNamespace != null ? () -> Block.fromNamespaceId(blockNamespace) : () -> null; } { - final Map armorProperties = element("armorProperties"); + final Properties armorProperties = main.section("armorProperties"); if (armorProperties != null) { - final String slot = (String) armorProperties.get("slot"); - switch (slot) { + switch (armorProperties.getString("slot")) { case "feet" -> this.equipmentSlot = EquipmentSlot.BOOTS; case "legs" -> this.equipmentSlot = EquipmentSlot.LEGGINGS; case "chest" -> this.equipmentSlot = EquipmentSlot.CHESTPLATE; @@ -335,206 +341,71 @@ public final class Registry { public @Nullable EquipmentSlot equipmentSlot() { return equipmentSlot; } - } - public static class EntityEntry extends Entry { - private final NamespaceID namespace; - private final int id; - private final String translationKey; - private final double width; - private final double height; - private final double drag; - private final double acceleration; - private final EntitySpawnType spawnType; - - private EntityEntry(String namespace, Map main, Map override) { - super(main, override); - this.namespace = NamespaceID.from(namespace); - this.id = getInt("id"); - this.translationKey = getString("translationKey"); - this.width = getDouble("width"); - this.height = getDouble("height"); - this.drag = getDouble("drag", 0.02); - this.acceleration = getDouble("acceleration", 0.08); - this.spawnType = EntitySpawnType.valueOf(getString("packetType").toUpperCase(Locale.ROOT)); - } - - public @NotNull NamespaceID namespace() { - return namespace; - } - - public int id() { - return id; - } - - public String translationKey() { - return translationKey; - } - - public double width() { - return width; - } - - public double height() { - return height; - } - - public double drag() { - return drag; - } - - public double acceleration() { - return acceleration; - } - - public EntitySpawnType spawnType() { - return spawnType; + @Override + public Properties custom() { + return custom; } } - public static class EnchantmentEntry extends Entry { - private final NamespaceID namespace; - private final int id; - private final String translationKey; - private final double maxLevel; - private final boolean isCursed; - private final boolean isDiscoverable; - private final boolean isTradeable; - private final boolean isTreasureOnly; - - private EnchantmentEntry(String namespace, Map main, Map override) { - super(main, override); - this.namespace = NamespaceID.from(namespace); - this.id = getInt("id"); - this.translationKey = getString("translationKey"); - this.maxLevel = getDouble("maxLevel"); - this.isCursed = getBoolean("curse", false); - this.isDiscoverable = getBoolean("discoverable", true); - this.isTradeable = getBoolean("tradeable", true); - this.isTreasureOnly = getBoolean("treasureOnly", false); - } - - public @NotNull NamespaceID namespace() { - return namespace; - } - - public int id() { - return id; - } - - public String translationKey() { - return translationKey; - } - - public double maxLevel() { - return maxLevel; - } - - public boolean isCursed() { - return isCursed; - } - - public boolean isDiscoverable() { - return isDiscoverable; - } - - public boolean isTradeable() { - return isTradeable; - } - - public boolean isTreasureOnly() { - return isTreasureOnly; + public record EntityEntry(NamespaceID namespace, int id, + String translationKey, + double width, double height, + double drag, double acceleration, + EntitySpawnType spawnType, + Properties custom) implements Entry { + public EntityEntry(String namespace, Properties main, Properties custom) { + this(NamespaceID.from(namespace), + main.getInt("id"), + main.getString("translationKey"), + main.getDouble("width"), + main.getDouble("height"), + main.getDouble("drag", 0.02), + main.getDouble("acceleration", 0.08), + EntitySpawnType.valueOf(main.getString("packetType").toUpperCase(Locale.ROOT)), + custom); } } - public static class PotionEffectEntry extends Entry { - private final NamespaceID namespace; - private final int id; - private final String translationKey; - private final int color; - private final boolean isInstantaneous; - - private PotionEffectEntry(String namespace, Map main, Map override) { - super(main, override); - this.namespace = NamespaceID.from(namespace); - this.id = getInt("id"); - this.translationKey = getString("translationKey"); - this.color = getInt("color"); - this.isInstantaneous = getBoolean("instantaneous"); - } - - public @NotNull NamespaceID namespace() { - return namespace; - } - - public int id() { - return id; - } - - public String translationKey() { - return translationKey; - } - - public int color() { - return color; - } - - public boolean isInstantaneous() { - return isInstantaneous; + public record EnchantmentEntry(NamespaceID namespace, int id, + String translationKey, + double maxLevel, + boolean isCursed, + boolean isDiscoverable, + boolean isTradeable, + boolean isTreasureOnly, + Properties custom) implements Entry { + public EnchantmentEntry(String namespace, Properties main, Properties custom) { + this(NamespaceID.from(namespace), + main.getInt("id"), + main.getString("translationKey"), + main.getDouble("maxLevel"), + main.getBoolean("isCursed", false), + main.getBoolean("isDiscoverable", true), + main.getBoolean("isTradeable", true), + main.getBoolean("isTreasureOnly", false), + custom); } } - public static class Entry { - private final Map main, override; - - private Entry(Map main, Map override) { - this.main = main; - this.override = override; + public record PotionEffectEntry(NamespaceID namespace, int id, + String translationKey, + int color, + boolean isInstantaneous, + Properties custom) implements Entry { + public PotionEffectEntry(String namespace, Properties main, Properties custom) { + this(NamespaceID.from(namespace), + main.getInt("id"), + main.getString("translationKey"), + main.getInt("color"), + main.getBoolean("instant"), + custom); } + } - public String getString(String name, String defaultValue) { - var element = element(name); - return element != null ? (String) element : defaultValue; - } - - public String getString(String name) { - return element(name); - } - - public double getDouble(String name, double defaultValue) { - var element = element(name); - return element != null ? ((Number) element).doubleValue() : defaultValue; - } - - public double getDouble(String name) { - return ((Number) element(name)).doubleValue(); - } - - public int getInt(String name, int defaultValue) { - var element = element(name); - return element != null ? ((Number) element).intValue() : defaultValue; - } - - public int getInt(String name) { - return ((Number) element(name)).intValue(); - } - - public boolean getBoolean(String name, boolean defaultValue) { - var element = element(name); - return element != null ? (boolean) element : defaultValue; - } - - public boolean getBoolean(String name) { - return element(name); - } - - protected T element(String name) { - Object result; - if (override != null && (result = override.get(name)) != null) { - return (T) result; - } - return (T) main.get(name); - } + public interface Entry { + @ApiStatus.Experimental + Properties custom(); } private static Object readObject(JsonReader reader) throws IOException { @@ -544,19 +415,117 @@ public final class Registry { reader.beginArray(); while (reader.hasNext()) list.add(readObject(reader)); reader.endArray(); - yield List.copyOf(list); + yield list; } case BEGIN_OBJECT -> { Map map = new HashMap<>(); reader.beginObject(); - while (reader.hasNext()) map.put(reader.nextName().intern(), readObject(reader)); + while (reader.hasNext()) map.put(reader.nextName(), readObject(reader)); reader.endObject(); - yield Map.copyOf(map); + yield map; } - case STRING -> reader.nextString().intern(); + case STRING -> reader.nextString(); case NUMBER -> ToNumberPolicy.LONG_OR_DOUBLE.readNumber(reader); case BOOLEAN -> reader.nextBoolean(); default -> throw new IllegalStateException("Invalid peek: " + reader.peek()); }; } + + record PropertiesMap(Map map) implements Properties { + @Override + public String getString(String name, String defaultValue) { + var element = element(name); + return element != null ? (String) element : defaultValue; + } + + @Override + public String getString(String name) { + return element(name); + } + + @Override + public double getDouble(String name, double defaultValue) { + var element = element(name); + return element != null ? ((Number) element).doubleValue() : defaultValue; + } + + @Override + public double getDouble(String name) { + return ((Number) element(name)).doubleValue(); + } + + @Override + public int getInt(String name, int defaultValue) { + var element = element(name); + return element != null ? ((Number) element).intValue() : defaultValue; + } + + @Override + public int getInt(String name) { + return ((Number) element(name)).intValue(); + } + + @Override + public boolean getBoolean(String name, boolean defaultValue) { + var element = element(name); + return element != null ? (boolean) element : defaultValue; + } + + @Override + public boolean getBoolean(String name) { + return element(name); + } + + @Override + public Properties section(String name) { + Map map = element(name); + if (map == null) return null; + return new PropertiesMap(map); + } + + @Override + public Map asMap() { + return map; + } + + private T element(String name) { + //noinspection unchecked + return (T) map.get(name); + } + } + + public interface Properties extends Iterable> { + static Properties fromMap(Map map) { + return new PropertiesMap(map); + } + + String getString(String name, String defaultValue); + + String getString(String name); + + double getDouble(String name, double defaultValue); + + double getDouble(String name); + + int getInt(String name, int defaultValue); + + int getInt(String name); + + boolean getBoolean(String name, boolean defaultValue); + + boolean getBoolean(String name); + + Properties section(String name); + + Map asMap(); + + @Override + default @NotNull Iterator> iterator() { + return asMap().entrySet().iterator(); + } + + default int size() { + return asMap().size(); + } + } } diff --git a/src/main/java/net/minestom/server/sound/SoundEventImpl.java b/src/main/java/net/minestom/server/sound/SoundEventImpl.java index 8430feedd..586633f19 100644 --- a/src/main/java/net/minestom/server/sound/SoundEventImpl.java +++ b/src/main/java/net/minestom/server/sound/SoundEventImpl.java @@ -8,7 +8,7 @@ import java.util.Collection; record SoundEventImpl(NamespaceID namespace, int id) implements SoundEvent { private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.SOUNDS, - (namespace, object) -> new SoundEventImpl(NamespaceID.from(namespace), ((Number) object.get("id")).intValue())); + (namespace, properties) -> new SoundEventImpl(NamespaceID.from(namespace), properties.getInt("id"))); static SoundEvent get(@NotNull String namespace) { return CONTAINER.get(namespace); diff --git a/src/main/java/net/minestom/server/statistic/StatisticTypeImpl.java b/src/main/java/net/minestom/server/statistic/StatisticTypeImpl.java index c53c9569b..ab770034d 100644 --- a/src/main/java/net/minestom/server/statistic/StatisticTypeImpl.java +++ b/src/main/java/net/minestom/server/statistic/StatisticTypeImpl.java @@ -8,7 +8,7 @@ import java.util.Collection; record StatisticTypeImpl(NamespaceID namespace, int id) implements StatisticType { private static final Registry.Container CONTAINER = Registry.createContainer(Registry.Resource.STATISTICS, - (namespace, object) -> new StatisticTypeImpl(NamespaceID.from(namespace), ((Number) object.get("id")).intValue())); + (namespace, properties) -> new StatisticTypeImpl(NamespaceID.from(namespace), properties.getInt("id"))); static StatisticType get(@NotNull String namespace) { return CONTAINER.get(namespace); diff --git a/src/main/java/net/minestom/server/utils/collection/MergedMap.java b/src/main/java/net/minestom/server/utils/collection/MergedMap.java new file mode 100644 index 000000000..1181dfdc3 --- /dev/null +++ b/src/main/java/net/minestom/server/utils/collection/MergedMap.java @@ -0,0 +1,90 @@ +package net.minestom.server.utils.collection; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Predicate; +import java.util.stream.Stream; + +@ApiStatus.Internal +public final class MergedMap extends AbstractMap { + private final Map first, second; + + public MergedMap(Map first, Map second) { + this.first = Objects.requireNonNull(first); + this.second = Objects.requireNonNull(second); + } + + // mandatory methods + + final Set> entrySet = new AbstractSet<>() { + @Override + public Iterator> iterator() { + return stream().iterator(); + } + + @Override + public int size() { + return (int) stream().count(); + } + + @Override + public Stream> stream() { + return Stream.concat(first.entrySet().stream(), secondStream()) + .map(e -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), e.getValue())); + } + + @Override + public Stream> parallelStream() { + return stream().parallel(); + } + + @Override + public Spliterator> spliterator() { + return stream().spliterator(); + } + }; + + Stream> secondStream() { + return second.entrySet().stream().filter(e -> !first.containsKey(e.getKey())); + } + + @Override + public Set> entrySet() { + return entrySet; + } + + // optimizations + + @Override + public boolean containsKey(Object key) { + return first.containsKey(key) || second.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return first.containsValue(value) || + secondStream().anyMatch(Predicate.isEqual(value)); + } + + @Override + public V get(Object key) { + V v = first.get(key); + return v != null ? v : second.get(key); + } + + @Override + public V getOrDefault(Object key, V defaultValue) { + V v = first.get(key); + return v != null ? v : second.getOrDefault(key, defaultValue); + } + + @Override + public void forEach(BiConsumer action) { + first.forEach(action); + second.forEach((k, v) -> { + if (!first.containsKey(k)) action.accept(k, v); + }); + } +}