Improve block#withProperty/ies performance

This commit is contained in:
TheMode 2021-07-19 23:59:40 +02:00
parent 14e42f7907
commit 4d2b925edd
3 changed files with 39 additions and 27 deletions

View File

@ -46,13 +46,7 @@ public interface Block extends ProtocolObject, TagReadable, BlockConstants {
* @see #withProperty(String, String)
*/
@Contract(pure = true)
default @NotNull Block withProperties(@NotNull Map<@NotNull String, @NotNull String> properties) {
Block block = this;
for (var entry : properties.entrySet()) {
block = block.withProperty(entry.getKey(), entry.getValue());
}
return block;
}
@NotNull Block withProperties(@NotNull Map<@NotNull String, @NotNull String> properties);
/**
* Creates a new block with a tag modified.

View File

@ -14,41 +14,63 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
class BlockImpl implements Block {
final class BlockImpl implements Block {
private static final Cache<NBTCompound, NBTCompound> NBT_CACHE = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofMinutes(5))
.weakValues()
.build();
private final Registry.BlockEntry registry;
private final BlockLoader.PropertyEntry propertyEntry;
private final Map<String, String> properties;
private final NBTCompound nbt;
private final BlockHandler handler;
BlockImpl(@NotNull Registry.BlockEntry registry,
@NotNull BlockLoader.PropertyEntry propertyEntry,
@NotNull Map<String, String> properties,
@Nullable NBTCompound nbt,
@Nullable BlockHandler handler) {
this.registry = registry;
this.propertyEntry = propertyEntry;
this.properties = Collections.unmodifiableMap(properties);
this.nbt = nbt;
this.handler = handler;
}
BlockImpl(@NotNull Registry.BlockEntry registry,
@NotNull BlockLoader.PropertyEntry propertyEntry,
@NotNull Map<String, String> properties) {
this(registry, properties, null, null);
this(registry, propertyEntry, properties, null, null);
}
@Override
public @NotNull Block withProperty(@NotNull String property, @NotNull String value) {
var properties = new HashMap<>(this.properties);
properties.put(property, value);
Block block = BlockLoader.getProperties(name(), properties);
Block block = propertyEntry.getProperties(properties);
if (block == null)
throw new IllegalArgumentException("Invalid property: " + property + ":" + value);
if (nbt != null || handler != null)
return new BlockImpl(block.registry(), block.properties(), nbt, handler);
return new BlockImpl(block.registry(), propertyEntry, block.properties(), nbt, handler);
return block;
}
@Override
public @NotNull Block withProperties(@NotNull Map<@NotNull String, @NotNull String> properties) {
Block block;
if (this.properties.size() == properties.size()) {
// Map should be complete
block = propertyEntry.getProperties(properties);
} else {
var newProperties = new HashMap<>(this.properties);
newProperties.putAll(properties);
block = propertyEntry.getProperties(newProperties);
}
if (block == null)
throw new IllegalArgumentException("Invalid properties: " + properties);
if (nbt != null || handler != null)
return new BlockImpl(block.registry(), propertyEntry, block.properties(), nbt, handler);
return block;
}
@ -57,12 +79,12 @@ class BlockImpl implements Block {
var compound = Objects.requireNonNullElseGet(nbt(), NBTCompound::new);
tag.write(compound, value);
final var nbt = compound.getSize() > 0 ? NBT_CACHE.get(compound, c -> compound) : null;
return new BlockImpl(registry, properties, nbt, handler);
return new BlockImpl(registry, propertyEntry, properties, nbt, handler);
}
@Override
public @NotNull Block withHandler(@Nullable BlockHandler handler) {
return new BlockImpl(registry, properties, nbt, handler);
return new BlockImpl(registry, propertyEntry, properties, nbt, handler);
}
@Override

View File

@ -26,8 +26,6 @@ class BlockLoader {
// Block namespace -> registry data
private static final Map<String, Block> NAMESPACE_MAP = new HashMap<>();
// Block namespace -> properties map to block access
private static final Map<String, PropertyEntry> BLOCK_PROPERTY_MAP = new HashMap<>();
// Block id -> registry data
private static final Int2ObjectMap<Block> BLOCK_ID_MAP = new Int2ObjectOpenHashMap<>();
// Block state -> block object
@ -49,11 +47,6 @@ class BlockLoader {
return BLOCK_STATE_MAP.get(stateId);
}
static @Nullable Block getProperties(String namespace, Map<String, String> properties) {
final var entry = BLOCK_PROPERTY_MAP.get(namespace);
return entry.propertyMap.get(properties);
}
static Collection<Block> values() {
return Collections.unmodifiableCollection(NAMESPACE_MAP.values());
}
@ -75,10 +68,6 @@ class BlockLoader {
});
}
private static class PropertyEntry {
private final Map<Map<String, String>, Block> propertyMap = new ConcurrentHashMap<>();
}
private static void retrieveState(String namespace, JsonObject object, JsonObject stateObject) {
PropertyEntry propertyEntry = new PropertyEntry();
stateObject.entrySet().forEach(stateEntry -> {
@ -86,10 +75,17 @@ class BlockLoader {
JsonObject stateOverride = stateEntry.getValue().getAsJsonObject();
final int stateId = stateOverride.get("stateId").getAsInt();
final var propertyMap = BlockUtils.parseProperties(query);
final Block block = new BlockImpl(Registry.block(object, stateOverride), propertyMap);
final Block block = new BlockImpl(Registry.block(object, stateOverride), propertyEntry, propertyMap);
BLOCK_STATE_MAP.put(stateId, block);
propertyEntry.propertyMap.put(propertyMap, block);
propertyEntry.map.put(propertyMap, block);
});
BLOCK_PROPERTY_MAP.put(namespace, propertyEntry);
}
protected static class PropertyEntry {
private final Map<Map<String, String>, Block> map = new ConcurrentHashMap<>();
public @Nullable Block getProperties(Map<String, String> properties) {
return map.get(properties);
}
}
}