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) * @see #withProperty(String, String)
*/ */
@Contract(pure = true) @Contract(pure = true)
default @NotNull Block withProperties(@NotNull Map<@NotNull String, @NotNull String> properties) { @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;
}
/** /**
* Creates a new block with a tag modified. * 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.Map;
import java.util.Objects; import java.util.Objects;
class BlockImpl implements Block { final class BlockImpl implements Block {
private static final Cache<NBTCompound, NBTCompound> NBT_CACHE = Caffeine.newBuilder() private static final Cache<NBTCompound, NBTCompound> NBT_CACHE = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofMinutes(5)) .expireAfterWrite(Duration.ofMinutes(5))
.weakValues() .weakValues()
.build(); .build();
private final Registry.BlockEntry registry; private final Registry.BlockEntry registry;
private final BlockLoader.PropertyEntry propertyEntry;
private final Map<String, String> properties; private final Map<String, String> properties;
private final NBTCompound nbt; private final NBTCompound nbt;
private final BlockHandler handler; private final BlockHandler handler;
BlockImpl(@NotNull Registry.BlockEntry registry, BlockImpl(@NotNull Registry.BlockEntry registry,
@NotNull BlockLoader.PropertyEntry propertyEntry,
@NotNull Map<String, String> properties, @NotNull Map<String, String> properties,
@Nullable NBTCompound nbt, @Nullable NBTCompound nbt,
@Nullable BlockHandler handler) { @Nullable BlockHandler handler) {
this.registry = registry; this.registry = registry;
this.propertyEntry = propertyEntry;
this.properties = Collections.unmodifiableMap(properties); this.properties = Collections.unmodifiableMap(properties);
this.nbt = nbt; this.nbt = nbt;
this.handler = handler; this.handler = handler;
} }
BlockImpl(@NotNull Registry.BlockEntry registry, BlockImpl(@NotNull Registry.BlockEntry registry,
@NotNull BlockLoader.PropertyEntry propertyEntry,
@NotNull Map<String, String> properties) { @NotNull Map<String, String> properties) {
this(registry, properties, null, null); this(registry, propertyEntry, properties, null, null);
} }
@Override @Override
public @NotNull Block withProperty(@NotNull String property, @NotNull String value) { public @NotNull Block withProperty(@NotNull String property, @NotNull String value) {
var properties = new HashMap<>(this.properties); var properties = new HashMap<>(this.properties);
properties.put(property, value); properties.put(property, value);
Block block = BlockLoader.getProperties(name(), properties); Block block = propertyEntry.getProperties(properties);
if (block == null) if (block == null)
throw new IllegalArgumentException("Invalid property: " + property + ":" + value); throw new IllegalArgumentException("Invalid property: " + property + ":" + value);
if (nbt != null || handler != null) 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; return block;
} }
@ -57,12 +79,12 @@ class BlockImpl implements Block {
var compound = Objects.requireNonNullElseGet(nbt(), NBTCompound::new); var compound = Objects.requireNonNullElseGet(nbt(), NBTCompound::new);
tag.write(compound, value); tag.write(compound, value);
final var nbt = compound.getSize() > 0 ? NBT_CACHE.get(compound, c -> compound) : null; 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 @Override
public @NotNull Block withHandler(@Nullable BlockHandler handler) { public @NotNull Block withHandler(@Nullable BlockHandler handler) {
return new BlockImpl(registry, properties, nbt, handler); return new BlockImpl(registry, propertyEntry, properties, nbt, handler);
} }
@Override @Override

View File

@ -26,8 +26,6 @@ class BlockLoader {
// Block namespace -> registry data // Block namespace -> registry data
private static final Map<String, Block> NAMESPACE_MAP = new HashMap<>(); 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 // Block id -> registry data
private static final Int2ObjectMap<Block> BLOCK_ID_MAP = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<Block> BLOCK_ID_MAP = new Int2ObjectOpenHashMap<>();
// Block state -> block object // Block state -> block object
@ -49,11 +47,6 @@ class BlockLoader {
return BLOCK_STATE_MAP.get(stateId); 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() { static Collection<Block> values() {
return Collections.unmodifiableCollection(NAMESPACE_MAP.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) { private static void retrieveState(String namespace, JsonObject object, JsonObject stateObject) {
PropertyEntry propertyEntry = new PropertyEntry(); PropertyEntry propertyEntry = new PropertyEntry();
stateObject.entrySet().forEach(stateEntry -> { stateObject.entrySet().forEach(stateEntry -> {
@ -86,10 +75,17 @@ class BlockLoader {
JsonObject stateOverride = stateEntry.getValue().getAsJsonObject(); JsonObject stateOverride = stateEntry.getValue().getAsJsonObject();
final int stateId = stateOverride.get("stateId").getAsInt(); final int stateId = stateOverride.get("stateId").getAsInt();
final var propertyMap = BlockUtils.parseProperties(query); 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); 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);
}
} }
} }