mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-26 12:36:07 +01:00
31699ae9a8
* Updated Upstream (Bukkit/CraftBukkit) Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: a6a9d2a4 Remove some old ApiStatus.Experimental annotations be72314c SPIGOT-7300, PR-829: Add new DamageSource API providing enhanced information about entity damage b252cf05 SPIGOT-7576, PR-970: Add methods in MushroomCow to change stew effects b1c689bd PR-902: Add Server#isLoggingIPs to get log-ips configuration 08f86d1c PR-971: Add Player methods for client-side potion effects 2e3024a9 PR-963: Add API for in-world structures a23292a7 SPIGOT-7530, PR-948: Improve Resource Pack API with new 1.20.3 functionality 1851857b SPIGOT-3071, PR-969: Add entity spawn method with spawn reason cde4c52a SPIGOT-5553, PR-964: Add EntityKnockbackEvent CraftBukkit Changes: 38fd4bd50 Fix accidentally renamed internal damage method 80f0ce4be SPIGOT-7300, PR-1180: Add new DamageSource API providing enhanced information about entity damage 7e43f3b16 SPIGOT-7581: Fix typo in BlockMushroom ea14b7d90 SPIGOT-7576, PR-1347: Add methods in MushroomCow to change stew effects 4c687f243 PR-1259: Add Server#isLoggingIPs to get log-ips configuration 22a541a29 Improve support for per-world game rules cb7dccce2 PR-1348: Add Player methods for client-side potion effects b8d6109f0 PR-1335: Add API for in-world structures 4398a1b5b SPIGOT-7577: Make CraftWindCharge#explode discard the entity e74107678 Fix Crafter maximum stack size 0bb0f4f6a SPIGOT-7530, PR-1314: Improve Resource Pack API with new 1.20.3 functionality 4949f556d SPIGOT-3071, PR-1345: Add entity spawn method with spawn reason 20ac73ca2 PR-1353: Fix Structure#place not working as documented with 0 palette 3c1b77871 SPIGOT-6911, PR-1349: Change max book length in CraftMetaBook 333701839 SPIGOT-7572: Bee nests generated without bees f48f4174c SPIGOT-5553, PR-1336: Add EntityKnockbackEvent
344 lines
15 KiB
Diff
344 lines
15 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Thu, 11 Mar 2021 20:05:44 -0800
|
|
Subject: [PATCH] Custom table implementation for blockstate state lookups
|
|
|
|
Testing some redstone intensive machines showed to bring about a 10%
|
|
improvement.
|
|
|
|
diff --git a/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..57d0cd3ad6f972e986c72a57f1a6e36003f190c2
|
|
--- /dev/null
|
|
+++ b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java
|
|
@@ -0,0 +1,160 @@
|
|
+package io.papermc.paper.util.table;
|
|
+
|
|
+import com.google.common.collect.Table;
|
|
+import net.minecraft.world.level.block.state.StateHolder;
|
|
+import net.minecraft.world.level.block.state.properties.Property;
|
|
+import java.util.Collection;
|
|
+import java.util.HashSet;
|
|
+import java.util.Map;
|
|
+import java.util.Set;
|
|
+
|
|
+public final class ZeroCollidingReferenceStateTable {
|
|
+
|
|
+ // upper 32 bits: starting index
|
|
+ // lower 32 bits: bitset for contained ids
|
|
+ protected final long[] this_index_table;
|
|
+ protected final Comparable<?>[] this_table;
|
|
+ protected final StateHolder<?, ?> this_state;
|
|
+
|
|
+ protected long[] index_table;
|
|
+ protected StateHolder<?, ?>[][] value_table;
|
|
+
|
|
+ public ZeroCollidingReferenceStateTable(final StateHolder<?, ?> state, final Map<Property<?>, Comparable<?>> this_map) {
|
|
+ this.this_state = state;
|
|
+ this.this_index_table = this.create_table(this_map.keySet());
|
|
+
|
|
+ int max_id = -1;
|
|
+ for (final Property<?> property : this_map.keySet()) {
|
|
+ final int id = lookup_vindex(property, this.this_index_table);
|
|
+ if (id > max_id) {
|
|
+ max_id = id;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ this.this_table = new Comparable[max_id + 1];
|
|
+ for (final Map.Entry<Property<?>, Comparable<?>> entry : this_map.entrySet()) {
|
|
+ this.this_table[lookup_vindex(entry.getKey(), this.this_index_table)] = entry.getValue();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void loadInTable(final Table<Property<?>, Comparable<?>, StateHolder<?, ?>> table,
|
|
+ final Map<Property<?>, Comparable<?>> this_map) {
|
|
+ final Set<Property<?>> combined = new HashSet<>(table.rowKeySet());
|
|
+ combined.addAll(this_map.keySet());
|
|
+
|
|
+ this.index_table = this.create_table(combined);
|
|
+
|
|
+ int max_id = -1;
|
|
+ for (final Property<?> property : combined) {
|
|
+ final int id = lookup_vindex(property, this.index_table);
|
|
+ if (id > max_id) {
|
|
+ max_id = id;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ this.value_table = new StateHolder[max_id + 1][];
|
|
+
|
|
+ final Map<Property<?>, Map<Comparable<?>, StateHolder<?, ?>>> map = table.rowMap();
|
|
+ for (final Property<?> property : map.keySet()) {
|
|
+ final Map<Comparable<?>, StateHolder<?, ?>> propertyMap = map.get(property);
|
|
+
|
|
+ final int id = lookup_vindex(property, this.index_table);
|
|
+ final StateHolder<?, ?>[] states = this.value_table[id] = new StateHolder[property.getPossibleValues().size()];
|
|
+
|
|
+ for (final Map.Entry<Comparable<?>, StateHolder<?, ?>> entry : propertyMap.entrySet()) {
|
|
+ if (entry.getValue() == null) {
|
|
+ // TODO what
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ states[((Property)property).getIdFor(entry.getKey())] = entry.getValue();
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ for (final Map.Entry<Property<?>, Comparable<?>> entry : this_map.entrySet()) {
|
|
+ final Property<?> property = entry.getKey();
|
|
+ final int index = lookup_vindex(property, this.index_table);
|
|
+
|
|
+ if (this.value_table[index] == null) {
|
|
+ this.value_table[index] = new StateHolder[property.getPossibleValues().size()];
|
|
+ }
|
|
+
|
|
+ this.value_table[index][((Property)property).getIdFor(entry.getValue())] = this.this_state;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ protected long[] create_table(final Collection<Property<?>> collection) {
|
|
+ int max_id = -1;
|
|
+ for (final Property<?> property : collection) {
|
|
+ final int id = property.getId();
|
|
+ if (id > max_id) {
|
|
+ max_id = id;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final long[] ret = new long[((max_id + 1) + 31) >>> 5]; // ceil((max_id + 1) / 32)
|
|
+
|
|
+ for (final Property<?> property : collection) {
|
|
+ final int id = property.getId();
|
|
+
|
|
+ ret[id >>> 5] |= (1L << (id & 31));
|
|
+ }
|
|
+
|
|
+ int total = 0;
|
|
+ for (int i = 1, len = ret.length; i < len; ++i) {
|
|
+ ret[i] |= (long)(total += Long.bitCount(ret[i - 1] & 0xFFFFFFFFL)) << 32;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ public Comparable<?> get(final Property<?> state) {
|
|
+ final Comparable<?>[] table = this.this_table;
|
|
+ final int index = lookup_vindex(state, this.this_index_table);
|
|
+
|
|
+ if (index < 0 || index >= table.length) {
|
|
+ return null;
|
|
+ }
|
|
+ return table[index];
|
|
+ }
|
|
+
|
|
+ public StateHolder<?, ?> get(final Property<?> property, final Comparable<?> with) {
|
|
+ final int withId = ((Property)property).getIdFor(with);
|
|
+ if (withId < 0) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ final int index = lookup_vindex(property, this.index_table);
|
|
+ final StateHolder<?, ?>[][] table = this.value_table;
|
|
+ if (index < 0 || index >= table.length) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ final StateHolder<?, ?>[] values = table[index];
|
|
+
|
|
+ if (withId >= values.length) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ return values[withId];
|
|
+ }
|
|
+
|
|
+ protected static int lookup_vindex(final Property<?> property, final long[] index_table) {
|
|
+ final int id = property.getId();
|
|
+ final long bitset_mask = (1L << (id & 31));
|
|
+ final long lower_mask = bitset_mask - 1;
|
|
+ final int index = id >>> 5;
|
|
+ if (index >= index_table.length) {
|
|
+ return -1;
|
|
+ }
|
|
+ final long index_value = index_table[index];
|
|
+ final long contains_check = ((index_value & bitset_mask) - 1) >> (Long.SIZE - 1); // -1L if doesn't contain
|
|
+
|
|
+ // index = total bits set in lower table values (upper 32 bits of index_value) plus total bits set in lower indices below id
|
|
+ // contains_check is 0 if the bitset had id set, else it's -1: so index is unaffected if contains_check == 0,
|
|
+ // otherwise it comes out as -1.
|
|
+ return (int)(((index_value >>> 32) + Long.bitCount(index_value & lower_mask)) | contains_check);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
|
|
index 89ba12d44865ecddebabbc39c769af4cd8c82702..5f285d190186a2ff5a61d05070593e1d633dd79a 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
|
|
@@ -39,11 +39,13 @@ public abstract class StateHolder<O, S> {
|
|
private final ImmutableMap<Property<?>, Comparable<?>> values;
|
|
private Table<Property<?>, Comparable<?>, S> neighbours;
|
|
protected final MapCodec<S> propertiesCodec;
|
|
+ protected final io.papermc.paper.util.table.ZeroCollidingReferenceStateTable optimisedTable; // Paper - optimise state lookup
|
|
|
|
protected StateHolder(O owner, ImmutableMap<Property<?>, Comparable<?>> entries, MapCodec<S> codec) {
|
|
this.owner = owner;
|
|
this.values = entries;
|
|
this.propertiesCodec = codec;
|
|
+ this.optimisedTable = new io.papermc.paper.util.table.ZeroCollidingReferenceStateTable(this, entries); // Paper - optimise state lookup
|
|
}
|
|
|
|
public <T extends Comparable<T>> S cycle(Property<T> property) {
|
|
@@ -84,11 +86,11 @@ public abstract class StateHolder<O, S> {
|
|
}
|
|
|
|
public <T extends Comparable<T>> boolean hasProperty(Property<T> property) {
|
|
- return this.values.containsKey(property);
|
|
+ return this.optimisedTable.get(property) != null; // Paper - optimise state lookup
|
|
}
|
|
|
|
public <T extends Comparable<T>> T getValue(Property<T> property) {
|
|
- Comparable<?> comparable = this.values.get(property);
|
|
+ Comparable<?> comparable = this.optimisedTable.get(property); // Paper - optimise state lookup
|
|
if (comparable == null) {
|
|
throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner);
|
|
} else {
|
|
@@ -97,24 +99,18 @@ public abstract class StateHolder<O, S> {
|
|
}
|
|
|
|
public <T extends Comparable<T>> Optional<T> getOptionalValue(Property<T> property) {
|
|
- Comparable<?> comparable = this.values.get(property);
|
|
+ Comparable<?> comparable = this.optimisedTable.get(property); // Paper - optimise state lookup
|
|
return comparable == null ? Optional.empty() : Optional.of(property.getValueClass().cast(comparable));
|
|
}
|
|
|
|
public <T extends Comparable<T>, V extends T> S setValue(Property<T> property, V value) {
|
|
- Comparable<?> comparable = this.values.get(property);
|
|
- if (comparable == null) {
|
|
- throw new IllegalArgumentException("Cannot set property " + property + " as it does not exist in " + this.owner);
|
|
- } else if (comparable.equals(value)) {
|
|
- return (S)this;
|
|
- } else {
|
|
- S object = this.neighbours.get(property, value);
|
|
- if (object == null) {
|
|
- throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value");
|
|
- } else {
|
|
- return object;
|
|
- }
|
|
+ // Paper start - optimise state lookup
|
|
+ final S ret = (S)this.optimisedTable.get(property, value);
|
|
+ if (ret == null) {
|
|
+ throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value");
|
|
}
|
|
+ return ret;
|
|
+ // Paper end - optimise state lookup
|
|
}
|
|
|
|
public <T extends Comparable<T>, V extends T> S trySetValue(Property<T> property, V value) {
|
|
@@ -147,7 +143,7 @@ public abstract class StateHolder<O, S> {
|
|
}
|
|
}
|
|
|
|
- this.neighbours = (Table<Property<?>, Comparable<?>, S>)(table.isEmpty() ? table : ArrayTable.create(table));
|
|
+ this.neighbours = (Table<Property<?>, Comparable<?>, S>)(table.isEmpty() ? table : ArrayTable.create(table)); this.optimisedTable.loadInTable((Table)this.neighbours, this.values); // Paper - optimise state lookup
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
|
|
index 8da64429eaf083578c672cd34f52dd42389ff396..bdd7d9c80eda872496272e46d8772634e44e622a 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
|
|
@@ -7,6 +7,13 @@ import java.util.Optional;
|
|
public class BooleanProperty extends Property<Boolean> {
|
|
private final ImmutableSet<Boolean> values = ImmutableSet.of(true, false);
|
|
|
|
+ // Paper start - optimise iblockdata state lookup
|
|
+ @Override
|
|
+ public final int getIdFor(final Boolean value) {
|
|
+ return value.booleanValue() ? 1 : 0;
|
|
+ }
|
|
+ // Paper end - optimise iblockdata state lookup
|
|
+
|
|
protected BooleanProperty(String name) {
|
|
super(name, Boolean.class);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
|
|
index 0a7a6c0e7635d0ada101074d4229df7edc6dd31b..e658d0b1a025192b48090e865b082ac4d99088d4 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
|
|
@@ -15,6 +15,15 @@ public class EnumProperty<T extends Enum<T> & StringRepresentable> extends Prope
|
|
private final ImmutableSet<T> values;
|
|
private final Map<String, T> names = Maps.newHashMap();
|
|
|
|
+ // Paper start - optimise iblockdata state lookup
|
|
+ private int[] idLookupTable;
|
|
+
|
|
+ @Override
|
|
+ public final int getIdFor(final T value) {
|
|
+ return this.idLookupTable[value.ordinal()];
|
|
+ }
|
|
+ // Paper end - optimise iblockdata state lookup
|
|
+
|
|
protected EnumProperty(String name, Class<T> type, Collection<T> values) {
|
|
super(name, type);
|
|
this.values = ImmutableSet.copyOf(values);
|
|
@@ -28,6 +37,14 @@ public class EnumProperty<T extends Enum<T> & StringRepresentable> extends Prope
|
|
this.names.put(string, enum_);
|
|
}
|
|
|
|
+ // Paper start - optimise iblockdata state lookup
|
|
+ int id = 0;
|
|
+ this.idLookupTable = new int[type.getEnumConstants().length];
|
|
+ java.util.Arrays.fill(this.idLookupTable, -1);
|
|
+ for (final T value : this.getPossibleValues()) {
|
|
+ this.idLookupTable[value.ordinal()] = id++;
|
|
+ }
|
|
+ // Paper end - optimise iblockdata state lookup
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
|
|
index cbe6e4579cc55a427dd8f8fe403478faf6d35b8f..765b1092d50608d479bbbe09c457999790ecf42c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
|
|
@@ -11,6 +11,16 @@ public class IntegerProperty extends Property<Integer> {
|
|
public final int min;
|
|
public final int max;
|
|
|
|
+ // Paper start - optimise iblockdata state lookup
|
|
+ @Override
|
|
+ public final int getIdFor(final Integer value) {
|
|
+ final int val = value.intValue();
|
|
+ final int ret = val - this.min;
|
|
+
|
|
+ return ret | ((this.max - ret) >> 31);
|
|
+ }
|
|
+ // Paper end - optimise iblockdata state lookup
|
|
+
|
|
protected IntegerProperty(String name, int min, int max) {
|
|
super(name, Integer.class);
|
|
if (min < 0) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
|
|
index 08c2e4ba9147fbea41e0fce26ad20586832a525b..1d8c3f2e57aa9b0830cf7cfb6354fcf636e9c74b 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
|
|
@@ -24,6 +24,17 @@ public abstract class Property<T extends Comparable<T>> {
|
|
}, this::getName);
|
|
private final Codec<Property.Value<T>> valueCodec = this.codec.xmap(this::value, Property.Value::value);
|
|
|
|
+ // Paper start - optimise iblockdata state lookup
|
|
+ private static final java.util.concurrent.atomic.AtomicInteger ID_GENERATOR = new java.util.concurrent.atomic.AtomicInteger();
|
|
+ private final int id = ID_GENERATOR.getAndIncrement();
|
|
+
|
|
+ public final int getId() {
|
|
+ return this.id;
|
|
+ }
|
|
+
|
|
+ public abstract int getIdFor(final T value);
|
|
+ // Paper end - optimise state lookup
|
|
+
|
|
protected Property(String name, Class<T> type) {
|
|
this.clazz = type;
|
|
this.name = name;
|