Files
MiraiMC/patches/server/0103-hydrogen.patch
2022-01-16 18:31:47 +01:00

1311 lines
45 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Simon Gardling <titaniumtown@gmail.com>
Date: Tue, 11 Jan 2022 18:07:35 -0500
Subject: [PATCH] hydrogen
Original code by CaffeineMC, licensed under GNU Lesser General Public License v3.0
You can find the original code on https://github.com/CaffeineMC/hydrogen-fabric (Yarn mappings)
This code specifically was taken from MeeniMc's PR implementing 1.18.x support here: https://github.com/CaffeineMC/hydrogen-fabric/pull/60
diff --git a/src/main/java/com/google/common/collect/HydrogenEntrySet.java b/src/main/java/com/google/common/collect/HydrogenEntrySet.java
new file mode 100644
index 0000000000000000000000000000000000000000..290ace0b7c8c6836fa61babf53aca5f2ae0959c1
--- /dev/null
+++ b/src/main/java/com/google/common/collect/HydrogenEntrySet.java
@@ -0,0 +1,36 @@
+package com.google.common.collect;
+
+import java.util.Map;
+
+class HydrogenEntrySet<K, V> extends ImmutableSet<Map.Entry<K, V>> {
+ private final K[] key;
+ private final V[] value;
+
+ private final int size;
+
+ HydrogenEntrySet(K[] key, V[] value, int size) {
+ this.key = key;
+ this.value = value;
+ this.size = size;
+ }
+
+ @Override
+ public UnmodifiableIterator<Map.Entry<K, V>> iterator() {
+ return new HydrogenEntrySetIterator<>(this.key, this.value, this.size);
+ }
+
+ @Override
+ public boolean contains(Object object) {
+ return false;
+ }
+
+ @Override
+ boolean isPartialView() {
+ return false;
+ }
+
+ @Override
+ public int size() {
+ return this.size;
+ }
+}
diff --git a/src/main/java/com/google/common/collect/HydrogenEntrySetIterator.java b/src/main/java/com/google/common/collect/HydrogenEntrySetIterator.java
new file mode 100644
index 0000000000000000000000000000000000000000..4346ae9da9fe01f89ed77dba37236d068e720aea
--- /dev/null
+++ b/src/main/java/com/google/common/collect/HydrogenEntrySetIterator.java
@@ -0,0 +1,41 @@
+package com.google.common.collect;
+
+import java.util.Map;
+
+class HydrogenEntrySetIterator<K, V> extends UnmodifiableIterator<Map.Entry<K, V>> {
+ private final K[] key;
+ private final V[] value;
+
+ private int remaining;
+ private int idx;
+
+ public HydrogenEntrySetIterator(K[] key, V[] value, int remaining) {
+ this.remaining = remaining;
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return this.remaining > 0;
+ }
+
+ @Override
+ public Map.Entry<K, V> next() {
+ this.skipEmpty();
+
+ Map.Entry<K, V> entry = new HydrogenImmutableMapEntry<>(this.key[this.idx],
+ this.value[this.idx]);
+
+ this.remaining--;
+ this.idx++;
+
+ return entry;
+ }
+
+ private void skipEmpty() {
+ while (this.key[this.idx] == null) {
+ this.idx++;
+ }
+ }
+}
diff --git a/src/main/java/com/google/common/collect/HydrogenImmutableMapEntry.java b/src/main/java/com/google/common/collect/HydrogenImmutableMapEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..c74ce1d2f8dfc6e50d2ffba462193093c7633c6c
--- /dev/null
+++ b/src/main/java/com/google/common/collect/HydrogenImmutableMapEntry.java
@@ -0,0 +1,28 @@
+package com.google.common.collect;
+
+import java.util.Map;
+
+public class HydrogenImmutableMapEntry<K, V> implements Map.Entry<K, V> {
+ private final K key;
+ private final V value;
+
+ public HydrogenImmutableMapEntry(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public K getKey() {
+ return this.key;
+ }
+
+ @Override
+ public V getValue() {
+ return this.value;
+ }
+
+ @Override
+ public V setValue(V value) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/com/google/common/collect/HydrogenImmutableReferenceHashMap.java b/src/main/java/com/google/common/collect/HydrogenImmutableReferenceHashMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..7772a4218fbc76a3f055881aa5301624b6961fe6
--- /dev/null
+++ b/src/main/java/com/google/common/collect/HydrogenImmutableReferenceHashMap.java
@@ -0,0 +1,134 @@
+package com.google.common.collect;
+
+import it.unimi.dsi.fastutil.Hash;
+import it.unimi.dsi.fastutil.HashCommon;
+
+import java.util.Map;
+
+import static it.unimi.dsi.fastutil.HashCommon.arraySize;
+
+@SuppressWarnings("unused")
+public class HydrogenImmutableReferenceHashMap<K, V> extends ImmutableMap<K, V> {
+ protected transient K[] key;
+ protected transient V[] value;
+ protected transient int mask;
+ protected transient int size;
+
+ public HydrogenImmutableReferenceHashMap() {
+
+ }
+
+ public HydrogenImmutableReferenceHashMap(Map<K, V> map) {
+ this(map.size(), Hash.DEFAULT_LOAD_FACTOR);
+
+ for (Map.Entry<K, V> entry : map.entrySet()) {
+ this.putInternal(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private HydrogenImmutableReferenceHashMap(final int size, final float loadFactor) {
+ if (loadFactor <= 0 || loadFactor > 1) {
+ throw new IllegalArgumentException("Load factor must be greater than 0 and smaller than or equal to 1");
+ }
+
+ if (size < 0) {
+ throw new IllegalArgumentException("The expected number of elements must be nonnegative");
+ }
+
+ int n = arraySize(size, loadFactor);
+
+ this.key = (K[]) new Object[n];
+ this.value = (V[]) new Object[n];
+ this.mask = n - 1;
+ this.size = size;
+ }
+
+ @Override
+ public int size() {
+ return this.size;
+ }
+
+ @Override
+ public V get(final Object k) {
+ int pos = HashCommon.mix(System.identityHashCode(k)) & this.mask;
+ K curr = this.key[pos];
+
+ // The starting point.
+ if (curr == null) {
+ return null;
+ } else if (k == curr) {
+ return this.value[pos];
+ }
+
+ // There's always an unused entry.
+ while (true) {
+ pos = pos + 1 & this.mask;
+ curr = this.key[pos];
+
+ if (curr == null) {
+ return null;
+ } else if (k == curr) {
+ return this.value[pos];
+ }
+ }
+ }
+
+ private void putInternal(final K k, final V v) {
+ final int pos = this.find(k);
+
+ if (pos < 0) {
+ int n = -pos - 1;
+
+ this.key[n] = k;
+ this.value[n] = v;
+ } else {
+ this.value[pos] = v;
+ }
+ }
+
+ private int find(final K k) {
+ int pos = HashCommon.mix(System.identityHashCode(k)) & this.mask;
+ K curr = this.key[pos];
+
+ // The starting point.
+ if (curr == null) {
+ return -(pos + 1);
+ } else if (k == curr) {
+ return pos;
+ }
+
+ // There's always an unused entry.
+ while (true) {
+ pos = pos + 1 & this.mask;
+ curr = this.key[pos];
+
+ if (curr == null) {
+ return -(pos + 1);
+ } else if (k == curr) {
+ return pos;
+ }
+ }
+ }
+
+ @Override
+ ImmutableSet<Entry<K, V>> createEntrySet() {
+ return new HydrogenEntrySet<>(this.key, this.value, this.size);
+ }
+
+ @Override
+ ImmutableSet<K> createKeySet() {
+ return new ImmutableMapKeySet<>(this);
+ }
+
+ @Override
+ ImmutableCollection<V> createValues() {
+ return new ImmutableMapValues<>(this);
+ }
+
+ @Override
+ boolean isPartialView() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/cache/StatePropertyTableCache.java b/src/main/java/me/jellysquid/mods/hydrogen/common/cache/StatePropertyTableCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..68c69bd90f5ecf3b72d631faac21f89b88fe37cb
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/cache/StatePropertyTableCache.java
@@ -0,0 +1,38 @@
+package me.jellysquid.mods.hydrogen.common.cache;
+
+import me.jellysquid.mods.hydrogen.common.collections.FastImmutableTableCache;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.Property;
+import net.minecraft.world.level.material.Fluid;
+import net.minecraft.world.level.material.FluidState;
+
+/**
+ * Many of the column and row key arrays in block state tables will be duplicated, leading to an unnecessary waste of
+ * memory. Since we have very limited options for trying to construct more optimized table types without throwing
+ * maintainability or mod compatibility out the window, this class acts as a dirty way to find and de-duplicate arrays
+ * after we construct our table types.
+ *
+ * While this global cache does not provide the ability to remove or clear entries from it, the reality is that it
+ * shouldn't matter because block state tables are only initialized once and remain loaded for the entire lifetime of
+ * the game. Even in the event of classloader pre-boot shenanigans, we still shouldn't leak memory as our cache will be
+ * dropped along with the rest of the loaded classes when the class loader is reaped.
+ */
+public class StatePropertyTableCache {
+ public static final FastImmutableTableCache<Property<?>, Comparable<?>, BlockState> BLOCK_STATE_TABLE =
+ new FastImmutableTableCache<>();
+
+ public static final FastImmutableTableCache<Property<?>, Comparable<?>, FluidState> FLUID_STATE_TABLE =
+ new FastImmutableTableCache<>();
+
+ @SuppressWarnings("unchecked")
+ public static <S, O> FastImmutableTableCache<Property<?>, Comparable<?>, S> getTableCache(O owner) {
+ if (owner instanceof Block) {
+ return (FastImmutableTableCache<Property<?>, Comparable<?>, S>) BLOCK_STATE_TABLE;
+ } else if (owner instanceof Fluid) {
+ return (FastImmutableTableCache<Property<?>, Comparable<?>, S>) FLUID_STATE_TABLE;
+ } else {
+ throw new IllegalArgumentException("");
+ }
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/collections/CollectionHelper.java b/src/main/java/me/jellysquid/mods/hydrogen/common/collections/CollectionHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f4dd657d1cb7c7dcf4bf4b9e213548adb1bae33
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/collections/CollectionHelper.java
@@ -0,0 +1,16 @@
+package me.jellysquid.mods.hydrogen.common.collections;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+public class CollectionHelper {
+ public static <T> List<T> fixed(List<T> src) {
+ return new FixedArrayList<>(src);
+ }
+
+ public static <T> Collector<T, ?, List<T>> toSizedList(int size) {
+ return Collectors.toCollection(() -> new ArrayList<>(size));
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/collections/FastImmutableTable.java b/src/main/java/me/jellysquid/mods/hydrogen/common/collections/FastImmutableTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..f87f4d1452dc65dc621493d6dcfaa678bf46069e
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/collections/FastImmutableTable.java
@@ -0,0 +1,227 @@
+package me.jellysquid.mods.hydrogen.common.collections;
+
+import com.google.common.collect.Table;
+import it.unimi.dsi.fastutil.Hash;
+import it.unimi.dsi.fastutil.HashCommon;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import static it.unimi.dsi.fastutil.HashCommon.arraySize;
+
+public class FastImmutableTable<R, C, V> implements Table<R, C, V> {
+ private R[] rowKeys;
+ private int[] rowIndices;
+ private final int rowMask;
+ private final int rowCount;
+
+ private C[] colKeys;
+ private int[] colIndices;
+ private final int colMask;
+ private final int colCount;
+
+ private V[] values;
+ private final int size;
+
+ @SuppressWarnings("unchecked")
+ public FastImmutableTable(Table<R, C, V> table, FastImmutableTableCache<R, C, V> cache) {
+ if (cache == null) {
+ throw new IllegalArgumentException("Cache must not be null");
+ }
+
+ float loadFactor = Hash.DEFAULT_LOAD_FACTOR;
+
+ Set<R> rowKeySet = table.rowKeySet();
+ Set<C> colKeySet = table.columnKeySet();
+
+ this.rowCount = rowKeySet.size();
+ this.colCount = colKeySet.size();
+
+ int rowN = arraySize(this.rowCount, loadFactor);
+ int colN = arraySize(this.colCount, loadFactor);
+
+ this.rowMask = rowN - 1;
+ this.rowKeys = (R[]) new Object[rowN];
+ this.rowIndices = new int[rowN];
+
+ this.colMask = colN - 1;
+ this.colKeys = (C[]) new Object[colN];
+ this.colIndices = new int[colN];
+
+ this.createIndex(this.colKeys, this.colIndices, this.colMask, colKeySet);
+ this.createIndex(this.rowKeys, this.rowIndices, this.rowMask, rowKeySet);
+
+ this.values = (V[]) new Object[this.rowCount * this.colCount];
+
+ for (Cell<R, C, V> cell : table.cellSet()) {
+ int colIdx = this.getIndex(this.colKeys, this.colIndices, this.colMask, cell.getColumnKey());
+ int rowIdx = this.getIndex(this.rowKeys, this.rowIndices, this.rowMask, cell.getRowKey());
+
+ if (colIdx < 0 || rowIdx < 0) {
+ throw new IllegalStateException("Missing index for " + cell);
+ }
+
+ this.values[this.colCount * rowIdx + colIdx] = cell.getValue();
+ }
+
+ this.size = table.size();
+
+ this.rowKeys = cache.dedupRows(this.rowKeys);
+ this.rowIndices = cache.dedupIndices(this.rowIndices);
+
+ this.colIndices = cache.dedupIndices(this.colIndices);
+ this.colKeys = cache.dedupColumns(this.colKeys);
+
+ this.values = cache.dedupValues(this.values);
+ }
+
+ private <T> void createIndex(T[] keys, int[] indices, int mask, Collection<T> iterable) {
+ int index = 0;
+
+ for (T obj : iterable) {
+ int i = this.find(keys, mask, obj);
+
+ if (i < 0) {
+ int pos = -i - 1;
+
+ keys[pos] = obj;
+ indices[pos] = index++;
+ }
+ }
+ }
+
+ private <T> int getIndex(T[] keys, int[] indices, int mask, T key) {
+ int pos = this.find(keys, mask, key);
+
+ if (pos < 0) {
+ return -1;
+ }
+
+ return indices[pos];
+ }
+
+ @Override
+ public boolean contains(Object rowKey, Object columnKey) {
+ return this.get(rowKey, columnKey) != null;
+ }
+
+ @Override
+ public boolean containsRow(Object rowKey) {
+ return this.find(this.rowKeys, this.rowMask, rowKey) >= 0;
+ }
+
+ @Override
+ public boolean containsColumn(Object columnKey) {
+ return this.find(this.colKeys, this.colMask, columnKey) >= 0;
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return ArrayUtils.contains(this.values, value);
+ }
+
+ @Override
+ public V get(Object rowKey, Object columnKey) {
+ final int row = this.getIndex(this.rowKeys, this.rowIndices, this.rowMask, rowKey);
+ final int col = this.getIndex(this.colKeys, this.colIndices, this.colMask, columnKey);
+
+ if (row < 0 || col < 0) {
+ return null;
+ }
+
+ return this.values[this.colCount * row + col];
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.size() == 0;
+ }
+
+ @Override
+ public int size() {
+ return this.size;
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public V put(R rowKey, C columnKey, V val) {
+ throw new UnsupportedOperationException();
+ }
+
+ private <T> int find(T[] key, int mask, T value) {
+ T curr;
+ int pos;
+ // The starting point.
+ if ((curr = key[pos = HashCommon.mix(value.hashCode()) & mask]) == null) {
+ return -(pos + 1);
+ }
+ if (value.equals(curr)) {
+ return pos;
+ }
+ // There's always an unused entry.
+ while (true) {
+ if ((curr = key[pos = pos + 1 & mask]) == null) {
+ return -(pos + 1);
+ }
+ if (value.equals(curr)) {
+ return pos;
+ }
+ }
+ }
+
+ @Override
+ public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public V remove(Object rowKey, Object columnKey) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<C, V> row(R rowKey) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<R, V> column(C columnKey) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<Cell<R, C, V>> cellSet() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<R> rowKeySet() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<C> columnKeySet() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<V> values() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<R, Map<C, V>> rowMap() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<C, Map<R, V>> columnMap() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/collections/FastImmutableTableCache.java b/src/main/java/me/jellysquid/mods/hydrogen/common/collections/FastImmutableTableCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..8fd17d2738be1cacedf56bb5c22ead9e473a9b99
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/collections/FastImmutableTableCache.java
@@ -0,0 +1,44 @@
+package me.jellysquid.mods.hydrogen.common.collections;
+
+import it.unimi.dsi.fastutil.Hash;
+import it.unimi.dsi.fastutil.ints.IntArrays;
+import it.unimi.dsi.fastutil.objects.ObjectArrays;
+import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
+
+/**
+ * @param <R> The type used by the
+ * @param <C>
+ * @param <V>
+ */
+public class FastImmutableTableCache<R, C, V> {
+ private final ObjectOpenCustomHashSet<R[]> rows;
+ private final ObjectOpenCustomHashSet<C[]> columns;
+ private final ObjectOpenCustomHashSet<V[]> values;
+
+ private final ObjectOpenCustomHashSet<int[]> indices;
+
+ @SuppressWarnings("unchecked")
+ public FastImmutableTableCache() {
+ this.rows = new ObjectOpenCustomHashSet<>((Hash.Strategy<R[]>) ObjectArrays.HASH_STRATEGY);
+ this.columns = new ObjectOpenCustomHashSet<>((Hash.Strategy<C[]>) ObjectArrays.HASH_STRATEGY);
+ this.values = new ObjectOpenCustomHashSet<>((Hash.Strategy<V[]>) ObjectArrays.HASH_STRATEGY);
+
+ this.indices = new ObjectOpenCustomHashSet<>(IntArrays.HASH_STRATEGY);
+ }
+
+ public synchronized V[] dedupValues(V[] values) {
+ return this.values.addOrGet(values);
+ }
+
+ public synchronized R[] dedupRows(R[] rows) {
+ return this.rows.addOrGet(rows);
+ }
+
+ public synchronized C[] dedupColumns(C[] columns) {
+ return this.columns.addOrGet(columns);
+ }
+
+ public synchronized int[] dedupIndices(int[] ints) {
+ return this.indices.addOrGet(ints);
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/collections/FixedArrayList.java b/src/main/java/me/jellysquid/mods/hydrogen/common/collections/FixedArrayList.java
new file mode 100644
index 0000000000000000000000000000000000000000..35e4c91e9b87d7fc8394b046c85b2b7f75afeb27
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/collections/FixedArrayList.java
@@ -0,0 +1,153 @@
+package me.jellysquid.mods.hydrogen.common.collections;
+
+import com.google.common.collect.Iterators;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.*;
+
+public class FixedArrayList<T> implements List<T> {
+ private final T[] array;
+
+ @SuppressWarnings("unchecked")
+ public FixedArrayList(List<T> list) {
+ this(list.toArray((T[]) new Object[0]));
+ }
+
+ public FixedArrayList(T[] array) {
+ this.array = array;
+ }
+
+ @Override
+ public int size() {
+ return this.array.length;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.array.length == 0;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return ArrayUtils.contains(this.array, o);
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return Iterators.forArray(this.array);
+ }
+
+ @Override
+ public Object[] toArray() {
+ return this.array.clone();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <T1> T1[] toArray(T1[] dst) {
+ T[] src = this.array;
+
+ if (dst.length < src.length) {
+ return (T1[]) Arrays.copyOf(src, src.length, dst.getClass());
+ }
+
+ System.arraycopy(src, 0, dst, 0, src.length);
+
+ if (dst.length > src.length) {
+ dst[src.length] = null;
+ }
+
+ return dst;
+ }
+
+ @Override
+ public boolean add(T t) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ for (Object o : c) {
+ if (!ArrayUtils.contains(this.array, o)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends T> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends T> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public T get(int index) {
+ return this.array[index];
+ }
+
+ @Override
+ public T set(int index, T element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void add(int index, T element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public T remove(int index) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ return ArrayUtils.indexOf(this.array, o);
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ return ArrayUtils.lastIndexOf(this.array, o);
+ }
+
+ @Override
+ public ListIterator<T> listIterator() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ListIterator<T> listIterator(int index) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<T> subList(int fromIndex, int toIndex) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/dedup/DeduplicationCache.java b/src/main/java/me/jellysquid/mods/hydrogen/common/dedup/DeduplicationCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4ebb3c59f2ddb63f39d0fdea62f41a042b68c25
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/dedup/DeduplicationCache.java
@@ -0,0 +1,56 @@
+package me.jellysquid.mods.hydrogen.common.dedup;
+
+import it.unimi.dsi.fastutil.Hash;
+import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
+
+import java.util.Objects;
+
+public class DeduplicationCache<T> {
+ private final ObjectOpenCustomHashSet<T> pool;
+
+ private int attemptedInsertions = 0;
+ private int deduplicated = 0;
+
+ public DeduplicationCache(Hash.Strategy<T> strategy) {
+ this.pool = new ObjectOpenCustomHashSet<>(strategy);
+ }
+
+ public DeduplicationCache() {
+ this.pool = new ObjectOpenCustomHashSet<>(new Hash.Strategy<T>() {
+ @Override
+ public int hashCode(T o) {
+ return Objects.hashCode(o);
+ }
+
+ @Override
+ public boolean equals(T a, T b) {
+ return Objects.equals(a, b);
+ }
+ });
+ }
+
+ public synchronized T deduplicate(T item) {
+ this.attemptedInsertions++;
+
+ T result = this.pool.addOrGet(item);
+
+ if (result != item) {
+ this.deduplicated++;
+ }
+
+ return result;
+ }
+
+ public synchronized void clearCache() {
+ this.attemptedInsertions = 0;
+ this.deduplicated = 0;
+
+ this.pool.clear();
+ }
+
+ @Override
+ public synchronized String toString() {
+ return String.format("DeduplicationCache ( %d/%d de-duplicated, %d pooled )",
+ this.deduplicated, this.attemptedInsertions, this.pool.size());
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/dedup/IdentifierCaches.java b/src/main/java/me/jellysquid/mods/hydrogen/common/dedup/IdentifierCaches.java
new file mode 100644
index 0000000000000000000000000000000000000000..69246b3e2944c273818c4ef4731d6b2f2e6d18fc
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/dedup/IdentifierCaches.java
@@ -0,0 +1,6 @@
+package me.jellysquid.mods.hydrogen.common.dedup;
+
+public class IdentifierCaches {
+ public static final DeduplicationCache<String> NAMESPACES = new DeduplicationCache<>();
+ public static final DeduplicationCache<String> PATH = new DeduplicationCache<>();
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/jvm/ClassConstructors.java b/src/main/java/me/jellysquid/mods/hydrogen/common/jvm/ClassConstructors.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b3a7c6e2ce171b5ab7444e16e75c1a53ff3b899
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/jvm/ClassConstructors.java
@@ -0,0 +1,44 @@
+package me.jellysquid.mods.hydrogen.common.jvm;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Constructor;
+import java.util.Map;
+
+@SuppressWarnings("unchecked")
+public class ClassConstructors {
+ private static MethodHandle FAST_IMMUTABLE_REFERENCE_HASH_MAP_CONSTRUCTOR;
+
+ public static void init() {
+ initGuavaExtensions();
+ }
+
+ private static void initGuavaExtensions() { // Note: maybe this isn't needed as this is no longer a mod, need to look into this further.
+ // define classes in their reverse link order to prevent duplicate definition issues
+ ClassDefineTool.defineClass(ImmutableMap.class, "com.google.common.collect.HydrogenImmutableMapEntry");
+ ClassDefineTool.defineClass(ImmutableMap.class, "com.google.common.collect.HydrogenEntrySetIterator");
+ ClassDefineTool.defineClass(ImmutableMap.class, "com.google.common.collect.HydrogenEntrySet");
+
+ Class<?> immutableRefHashMapClass = ClassDefineTool.defineClass(ImmutableMap.class, "com.google.common.collect.HydrogenImmutableReferenceHashMap");
+
+ try {
+ FAST_IMMUTABLE_REFERENCE_HASH_MAP_CONSTRUCTOR = MethodHandles.lookup()
+ .findConstructor(immutableRefHashMapClass, MethodType.methodType(Void.TYPE, Map.class))
+ // compiler can only generate a desc returning ImmutableMap below
+ .asType(MethodType.methodType(ImmutableMap.class, Map.class));
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException("Failed to find constructor", e);
+ }
+ }
+
+ public static <K, V> ImmutableMap<K, V> createFastImmutableMap(Map<K, V> orig) {
+ try {
+ return (ImmutableMap<K, V>) FAST_IMMUTABLE_REFERENCE_HASH_MAP_CONSTRUCTOR.invokeExact((Map<K, V>) orig);
+ } catch (Throwable e) {
+ throw new RuntimeException("Could not instantiate collection", e);
+ }
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/jvm/ClassDefineTool.java b/src/main/java/me/jellysquid/mods/hydrogen/common/jvm/ClassDefineTool.java
new file mode 100644
index 0000000000000000000000000000000000000000..db7d91dfb8b852e1c204c44f62847f48261371f7
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/jvm/ClassDefineTool.java
@@ -0,0 +1,44 @@
+package me.jellysquid.mods.hydrogen.common.jvm;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.net.URL;
+
+public class ClassDefineTool {
+ private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+
+ private static final Logger LOGGER = LogManager.getLogger("Hydrogen");
+
+ public static Class<?> defineClass(Class<?> context, String name) {
+ String path = "/" + name.replace('.', '/') + ".class";
+ URL url = ClassDefineTool.class.getResource(path);
+
+ if (url == null) {
+ throw new RuntimeException("Couldn't find resource: " + path);
+ }
+
+ LOGGER.info("Injecting class '{}' (url: {})", name, url);
+
+ byte[] code;
+
+ try {
+ code = IOUtils.toByteArray(url);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not read class bytes from resources: " + path, e);
+ }
+
+ try {
+ // The context class need to be in a module that exports and opens to ClassDefineTool
+ // Example: guava's automatic module exports and opens to everything
+ MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(context, LOOKUP);
+ return privateLookup.defineClass(code);
+ } catch (Throwable throwable) {
+ throw new RuntimeException("Failed to define class", throwable);
+ }
+ }
+
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/state/all/AllMatchOneBoolean.java b/src/main/java/me/jellysquid/mods/hydrogen/common/state/all/AllMatchOneBoolean.java
new file mode 100644
index 0000000000000000000000000000000000000000..55844de0ca4247c165a49b307f66d0d141134b2a
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/state/all/AllMatchOneBoolean.java
@@ -0,0 +1,46 @@
+package me.jellysquid.mods.hydrogen.common.state.all;
+
+import me.jellysquid.mods.hydrogen.common.state.single.SingleMatchOne;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.Property;
+import java.util.List;
+import java.util.function.Predicate;
+
+public class AllMatchOneBoolean implements Predicate<BlockState> {
+ private final Property<?>[] properties;
+ private final boolean[] values;
+
+ public AllMatchOneBoolean(List<Predicate<BlockState>> list) {
+ int size = list.size();
+
+ this.properties = new Property[size];
+ this.values = new boolean[size];
+
+ for (int i = 0; i < size; i++) {
+ SingleMatchOne predicate = (SingleMatchOne) list.get(i);
+
+ this.properties[i] = predicate.property;
+ this.values[i] = (boolean) predicate.value;
+ }
+ }
+
+ public static boolean canReplace(List<Predicate<BlockState>> list) {
+ return list.stream()
+ .allMatch(p -> {
+ return p instanceof SingleMatchOne && ((SingleMatchOne) p).value instanceof Boolean;
+ });
+ }
+
+ @Override
+ public boolean test(BlockState blockState) {
+ for (int i = 0; i < this.properties.length; i++) {
+ Boolean value = (Boolean) blockState.getValue(this.properties[i]);
+
+ if (value != this.values[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/state/all/AllMatchOneObject.java b/src/main/java/me/jellysquid/mods/hydrogen/common/state/all/AllMatchOneObject.java
new file mode 100644
index 0000000000000000000000000000000000000000..49be1c9d9da46dee2a14a5423d25a09ccfb19e3a
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/state/all/AllMatchOneObject.java
@@ -0,0 +1,37 @@
+package me.jellysquid.mods.hydrogen.common.state.all;
+
+import me.jellysquid.mods.hydrogen.common.state.single.SingleMatchOne;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.Property;
+import java.util.List;
+import java.util.function.Predicate;
+
+public class AllMatchOneObject implements Predicate<BlockState> {
+ private final Property<?>[] properties;
+ private final Object[] values;
+
+ public AllMatchOneObject(List<Predicate<BlockState>> list) {
+ int size = list.size();
+
+ this.properties = new Property[size];
+ this.values = new Object[size];
+
+ for (int i = 0; i < size; i++) {
+ SingleMatchOne predicate = (SingleMatchOne) list.get(i);
+
+ this.properties[i] = predicate.property;
+ this.values[i] = predicate.value;
+ }
+ }
+
+ @Override
+ public boolean test(BlockState blockState) {
+ for (int i = 0; i < this.properties.length; i++) {
+ if (blockState.getValue(this.properties[i]) != this.values[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/state/any/AllMatchAnyObject.java b/src/main/java/me/jellysquid/mods/hydrogen/common/state/any/AllMatchAnyObject.java
new file mode 100644
index 0000000000000000000000000000000000000000..b46cdb3fd8131d3ce570ed9fadae2c790645eba6
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/state/any/AllMatchAnyObject.java
@@ -0,0 +1,39 @@
+package me.jellysquid.mods.hydrogen.common.state.any;
+
+import me.jellysquid.mods.hydrogen.common.state.single.SingleMatchAny;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.Property;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+public class AllMatchAnyObject implements Predicate<BlockState> {
+ private final Property<?>[] properties;
+ private final Object[][] values;
+
+ public AllMatchAnyObject(List<Predicate<BlockState>> list) {
+ int size = list.size();
+
+ this.properties = new Property[size];
+ this.values = new Object[size][];
+
+ for (int i = 0; i < size; i++) {
+ SingleMatchAny predicate = (SingleMatchAny) list.get(i);
+
+ this.properties[i] = predicate.property;
+ this.values[i] = predicate.values;
+ }
+ }
+
+ @Override
+ public boolean test(BlockState blockState) {
+ for (int i = 0; i < this.properties.length; i++) {
+ if (!ArrayUtils.contains(this.values[i], blockState.getValue(this.properties[i]))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/state/single/SingleMatchAny.java b/src/main/java/me/jellysquid/mods/hydrogen/common/state/single/SingleMatchAny.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ba47d431bb53cb1ccb32cff4663d26466b53a33
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/state/single/SingleMatchAny.java
@@ -0,0 +1,63 @@
+package me.jellysquid.mods.hydrogen.common.state.single;
+
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.Property;
+
+public class SingleMatchAny implements Predicate<BlockState> {
+ public static final ObjectOpenHashSet<SingleMatchAny> PREDICATES = new ObjectOpenHashSet<>();
+
+ public final Property<?> property;
+ public final Object[] values;
+
+ private SingleMatchAny(Property<?> property, List<Object> values) {
+ this.property = property;
+ this.values = values.toArray();
+ }
+
+ public static SingleMatchAny create(Property<?> property, List<Object> values) {
+ return PREDICATES.addOrGet(new SingleMatchAny(property, values));
+ }
+
+ public static boolean areOfType(List<Predicate<BlockState>> predicates) {
+ return predicates.stream()
+ .allMatch(p -> {
+ return p instanceof SingleMatchAny;
+ });
+ }
+
+ public static boolean valuesMatchType(List<Predicate<BlockState>> predicates, Class<?> type) {
+ return predicates.stream()
+ .allMatch(p -> {
+ return p instanceof SingleMatchAny &&
+ Arrays.stream(((SingleMatchAny) p).values).allMatch(t -> type.isInstance(p));
+ });
+ }
+
+ @Override
+ public boolean test(BlockState blockState) {
+ return ArrayUtils.contains(this.values, blockState.getValue(this.property));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SingleMatchAny that = (SingleMatchAny) o;
+ return Objects.equals(property, that.property) &&
+ Arrays.equals(values, that.values);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(property);
+ result = 31 * result + Arrays.hashCode(values);
+ return result;
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/state/single/SingleMatchOne.java b/src/main/java/me/jellysquid/mods/hydrogen/common/state/single/SingleMatchOne.java
new file mode 100644
index 0000000000000000000000000000000000000000..18649ef152ea6f7ae08bfa217fd1a1ad6581245e
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/state/single/SingleMatchOne.java
@@ -0,0 +1,35 @@
+package me.jellysquid.mods.hydrogen.common.state.single;
+
+import java.util.List;
+import java.util.function.Predicate;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.block.state.properties.Property;
+
+public class SingleMatchOne implements Predicate<BlockState> {
+ public final Property<?> property;
+ public final Object value;
+
+ public SingleMatchOne(Property<?> property, Object value) {
+ this.property = property;
+ this.value = value;
+ }
+
+ public static boolean areOfType(List<Predicate<BlockState>> predicates) {
+ return predicates.stream()
+ .allMatch(p -> {
+ return p instanceof SingleMatchOne;
+ });
+ }
+
+ public static boolean valuesMatchType(List<Predicate<BlockState>> predicates, Class<?> type) {
+ return predicates.stream()
+ .allMatch(p -> {
+ return p instanceof SingleMatchOne && type.isInstance(((SingleMatchOne) p).value);
+ });
+ }
+
+ @Override
+ public boolean test(BlockState blockState) {
+ return blockState.getValue(this.property) == this.value;
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/util/AllPredicate.java b/src/main/java/me/jellysquid/mods/hydrogen/common/util/AllPredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4836a1a6616072b2cc5cc78a7b4902ca92f99b6
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/util/AllPredicate.java
@@ -0,0 +1,22 @@
+package me.jellysquid.mods.hydrogen.common.util;
+
+import java.util.function.Predicate;
+
+public class AllPredicate<T> implements Predicate<T> {
+ private final Predicate<T>[] predicates;
+
+ public AllPredicate(Predicate<T>[] predicates) {
+ this.predicates = predicates;
+ }
+
+ @Override
+ public boolean test(T t) {
+ for (Predicate<T> predicate : this.predicates) {
+ if (!predicate.test(t)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/me/jellysquid/mods/hydrogen/common/util/AnyPredicate.java b/src/main/java/me/jellysquid/mods/hydrogen/common/util/AnyPredicate.java
new file mode 100644
index 0000000000000000000000000000000000000000..6877466f27fe8a22e2a68cf954dfaf313f4db94e
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/hydrogen/common/util/AnyPredicate.java
@@ -0,0 +1,22 @@
+package me.jellysquid.mods.hydrogen.common.util;
+
+import java.util.function.Predicate;
+
+public class AnyPredicate<T> implements Predicate<T> {
+ private final Predicate<T>[] predicates;
+
+ public AnyPredicate(Predicate<T>[] predicates) {
+ this.predicates = predicates;
+ }
+
+ @Override
+ public boolean test(T t) {
+ for (Predicate<T> predicate : this.predicates) {
+ if (predicate.test(t)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/net/minecraft/resources/ResourceLocation.java b/src/main/java/net/minecraft/resources/ResourceLocation.java
index 6e5d13e63c97cb95b93af1d997dc0eb53f966566..231448a1a49f1c4034ce09e06dcbdabe103bd8dd 100644
--- a/src/main/java/net/minecraft/resources/ResourceLocation.java
+++ b/src/main/java/net/minecraft/resources/ResourceLocation.java
@@ -18,6 +18,7 @@ import net.minecraft.ResourceLocationException;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.util.GsonHelper;
import org.apache.commons.lang3.StringUtils;
+import me.jellysquid.mods.hydrogen.common.dedup.IdentifierCaches; // JettPack
public class ResourceLocation implements Comparable<ResourceLocation> {
public static final Codec<ResourceLocation> CODEC = Codec.STRING.comapFlatMap(ResourceLocation::read, ResourceLocation::toString).stable();
@@ -29,8 +30,10 @@ public class ResourceLocation implements Comparable<ResourceLocation> {
protected final String path;
protected ResourceLocation(String[] id) {
- this.namespace = StringUtils.isEmpty(id[0]) ? "minecraft" : id[0];
- this.path = id[1];
+ // JettPack start - port Hydrogen
+ this.namespace = IdentifierCaches.NAMESPACES.deduplicate(StringUtils.isEmpty(id[0]) ? "minecraft" : id[0]);
+ this.path = IdentifierCaches.PATH.deduplicate(id[1]);
+ // JettPack end
if (!isValidNamespace(this.namespace)) {
throw new ResourceLocationException("Non [a-z0-9_.-] character in namespace of location: " + org.apache.commons.lang3.StringUtils.normalizeSpace(this.namespace) + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(this.path)); // Paper
} else if (!isValidPath(this.path)) {
diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
index 2f5c021b6849acb81064d55418707921424b649b..8cf835307c36fb048d62af9f417daeeeb71a0cff 100644
--- a/src/main/java/net/minecraft/server/Main.java
+++ b/src/main/java/net/minecraft/server/Main.java
@@ -64,6 +64,7 @@ public class Main {
@DontObfuscate
public static void main(final OptionSet optionset) { // CraftBukkit - replaces main(String[] astring)
+ me.jellysquid.mods.hydrogen.common.jvm.ClassConstructors.init(); // JettPack
SharedConstants.tryDetectVersion();
/* CraftBukkit start - Replace everything
OptionParser optionparser = new OptionParser();
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 5be5eabc222b9e20c083ff83fae52010b19ea854..c263220bc504bdb2067ac8920fceac6de1bb850e 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
@@ -44,7 +44,7 @@ public abstract class StateHolder<O, S> {
protected StateHolder(O owner, ImmutableMap<Property<?>, Comparable<?>> entries, MapCodec<S> codec) {
this.owner = owner;
- this.values = entries;
+ this.values = me.jellysquid.mods.hydrogen.common.jvm.ClassConstructors.createFastImmutableMap(entries); // JettPack - port hydrogen
this.propertiesCodec = codec;
this.optimisedTable = new io.papermc.paper.util.table.ZeroCollidingReferenceStateTable(this, entries); // Paper - optimise state lookup
}