Yatopia/patches/server/0060-Port-hydrogen.patch

476 lines
19 KiB
Diff
Raw Normal View History

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: JellySquid <jellysquid+atwork@protonmail.com>
Date: Wed, 20 Jan 2021 20:14:55 +0100
Subject: [PATCH] Port hydrogen
2021-02-08 16:55:54 +01:00
Original code by JellySquid, licensed under GNU Lesser General Public License v3.0
you can find the original code on https://github.com/CaffeineMC/hydrogen-fabric/ (Yarn mappings)
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/cache/StatePropertyTableCache.java b/src/main/java/me/jellysquid/mods/lithium/common/cache/StatePropertyTableCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd18bb8b3550d0fe9ba2589151baceb9100ae6f2
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/cache/StatePropertyTableCache.java
@@ -0,0 +1,38 @@
+package me.jellysquid.mods.lithium.common.cache;
+
+import me.jellysquid.mods.lithium.common.collections.FastImmutableTableCache;
+import net.minecraft.server.Block;
+import net.minecraft.server.Fluid;
+import net.minecraft.server.FluidType;
+import net.minecraft.server.IBlockData;
+import net.minecraft.server.IBlockState;
+
+/**
+ * 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.
+ * <p>
+ * 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<IBlockState<?>, Comparable<?>, IBlockData> BLOCK_STATE_TABLE =
+ new FastImmutableTableCache<>();
+
+ public static final FastImmutableTableCache<IBlockState<?>, Comparable<?>, Fluid> FLUID_STATE_TABLE =
+ new FastImmutableTableCache<>();
+
+ @SuppressWarnings("unchecked")
+ public static <S, O> FastImmutableTableCache<IBlockState<?>, Comparable<?>, S> getTableCache(O owner) {
+ if (owner instanceof Block) {
+ return (FastImmutableTableCache<IBlockState<?>, Comparable<?>, S>) BLOCK_STATE_TABLE;
+ } else if (owner instanceof FluidType) {
+ return (FastImmutableTableCache<IBlockState<?>, Comparable<?>, S>) FLUID_STATE_TABLE;
+ } else {
+ throw new IllegalArgumentException("");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/collections/FastImmutableTable.java b/src/main/java/me/jellysquid/mods/lithium/common/collections/FastImmutableTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..9aa45ecdff223258c4f3252a61b9ccbbed6151d2
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/collections/FastImmutableTable.java
@@ -0,0 +1,227 @@
+package me.jellysquid.mods.lithium.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();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/jellysquid/mods/lithium/common/collections/FastImmutableTableCache.java b/src/main/java/me/jellysquid/mods/lithium/common/collections/FastImmutableTableCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5314a0396f4a8f373d855e873820ddddd635a4a
--- /dev/null
+++ b/src/main/java/me/jellysquid/mods/lithium/common/collections/FastImmutableTableCache.java
@@ -0,0 +1,44 @@
+package me.jellysquid.mods.lithium.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/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
index 7b8036840dcca16904b3063c209d5ff10ab8a6af..6bc5fedc4d8effa530799eac814f626dfeed2105 100644
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
@@ -336,6 +336,14 @@ public class Chunk implements IChunkAccess {
// CraftBukkit start
this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this);
+ // Yatopia start - Port hydrogen
+ // Upgrading a ProtoChunk to a Chunk might result in empty sections being copied over
+ // These simply waste memory, and the Chunk will return air blocks for any absent section without issue.
+ for (int i2 = 0; i2 < this.sections.length; i2++) {
+ if (ChunkSection.isEmpty(this.sections[i2])) {
+ this.sections[i2] = null;
+ }
+ } // Yatopia end
}
public org.bukkit.Chunk bukkitChunk;
diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
index 5f04591193d58ba7897194142da5efcbec3763dd..bf4f34b5f4f7473921589b2f001570a38e96c5ce 100644
--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java
+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java
@@ -70,8 +70,10 @@ public class ChunkRegionLoader {
private static final String UNINITIALISED_SKYLIGHT_TAG = "starlight.skylight_uninit";
private static final String STARLIGHT_VERSION_TAG = "starlight.light_version";
// Tuinity end - rewrite light engine
+ private static final ThreadLocal<NBTTagCompound> CAPTURED_TAGS = new ThreadLocal<>(); // Yatopia - Port Hydrogen
public static InProgressChunkHolder loadChunk(WorldServer worldserver, DefinedStructureManager definedstructuremanager, VillagePlace villageplace, ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound, boolean distinguish) {
+ CAPTURED_TAGS.set(nbttagcompound); // Yatopia
ArrayDeque<Runnable> tasksToExecuteOnMain = new ArrayDeque<>();
// Paper end
ChunkGenerator chunkgenerator = worldserver.getChunkProvider().getChunkGenerator();
@@ -190,10 +192,22 @@ public class ChunkRegionLoader {
} else {
object2 = protochunkticklist1;
}
+ // Yatopia start
+ NBTTagCompound strippedTag = new NBTTagCompound();
+ strippedTag.set("Entities", nbttagcompound1.getList("Entities", 10));
+ strippedTag.set("TileEntities", nbttagcompound1.getList("TileEntities", 10));
+ strippedTag.set("ChunkBukkitValues", nbttagcompound1.get("ChunkBukkitValues"));
+ // Yatopia end
object = new Chunk(worldserver.getMinecraftWorld(), chunkcoordintpair, biomestorage, chunkconverter, (TickList) object1, (TickList) object2, j, achunksection, // Paper start - fix massive nbt memory leak due to lambda. move lambda into a container method to not leak scope. Only clone needed NBT keys.
+ // Yatopia start - Port hydrogen
+ /*
createLoadEntitiesConsumer(new SafeNBTCopy(nbttagcompound1, "TileEntities", "Entities", "ChunkBukkitValues")) // Paper - move CB Chunk PDC into here
+ */
+ createLoadEntitiesConsumer(strippedTag)
+ // Yatopia end
);// Paper end
+ CAPTURED_TAGS.remove(); // Yatopia
((Chunk)object).setBlockNibbles(blockNibbles); // Tuinity - replace light impl
((Chunk)object).setSkyNibbles(skyNibbles); // Tuinity - replace light impl
} else {
diff --git a/src/main/java/net/minecraft/server/DataBits.java b/src/main/java/net/minecraft/server/DataBits.java
index f0c9009fb808ca664a7c3ebaeb8cfa8e2ba7b97e..40e3001bc1742231dcd02cee4423c57d0fd9a991 100644
--- a/src/main/java/net/minecraft/server/DataBits.java
+++ b/src/main/java/net/minecraft/server/DataBits.java
@@ -64,6 +64,7 @@ public class DataBits {
return j1;
}
+ public final void set(int i, int j){ b(i, j); } // Yatopia - OBFHELPER
public final void b(int i, int j) { // Paper - make final for inline
//Validate.inclusiveBetween(0L, (long) (this.e - 1), (long) i); // Paper
//Validate.inclusiveBetween(0L, this.d, (long) j); // Paper
@@ -74,6 +75,7 @@ public class DataBits {
this.b[k] = l & ~(this.d << i1) | ((long) j & this.d) << i1;
}
+ public final int get(int i) { return this.a(i); } // Yatopia - OBFHELPER
public final int a(int i) { // Paper - make final for inline
//Validate.inclusiveBetween(0L, (long) (this.e - 1), (long) i); // Paper
int j = this.b(i);
@@ -88,6 +90,7 @@ public class DataBits {
return this.b;
}
+ public int getSize(){ return b(); } // Yatopia - OBFHELPER
public int b() {
return this.e;
}
diff --git a/src/main/java/net/minecraft/server/IBlockDataHolder.java b/src/main/java/net/minecraft/server/IBlockDataHolder.java
index b19c694cf01bc868dd7c4ec6432b613d19f2ca40..865fd6ef879943634ee2861c6da21ead31306eb2 100644
--- a/src/main/java/net/minecraft/server/IBlockDataHolder.java
+++ b/src/main/java/net/minecraft/server/IBlockDataHolder.java
@@ -8,6 +8,10 @@ import com.google.common.collect.Table;
import com.google.common.collect.UnmodifiableIterator;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
+// Yatopia start
+import me.jellysquid.mods.lithium.common.cache.StatePropertyTableCache;
+import me.jellysquid.mods.lithium.common.collections.FastImmutableTable;
+// Yatopia end
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -143,7 +147,12 @@ public abstract class IBlockDataHolder<O, S> {
}
}
+ // Yatopia start - Port hydrogen
+ /*
this.e = (Table) (table.isEmpty() ? table : ArrayTable.create(table));
+ */
+ this.e = new FastImmutableTable<>((table.isEmpty() ? table : ArrayTable.create(table)), StatePropertyTableCache.getTableCache(this.c));
+ // Yatopia end
}
}
diff --git a/src/main/java/net/minecraft/server/NBTTagCompound.java b/src/main/java/net/minecraft/server/NBTTagCompound.java
index 06d5acab794e3ee139a11f9b068e8a359c46db2c..2bc71833fea91f1b7b35a431b1d6905a5738e83c 100644
--- a/src/main/java/net/minecraft/server/NBTTagCompound.java
+++ b/src/main/java/net/minecraft/server/NBTTagCompound.java
@@ -20,6 +20,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
+import it.unimi.dsi.fastutil.objects.Object2ObjectMap; // Yatopia
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; // Paper
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -74,7 +75,12 @@ public class NBTTagCompound implements NBTBase {
public final Map<String, NBTBase> map; // Paper
protected NBTTagCompound(Map<String, NBTBase> map) {
+ // Yatopia start - Port hydrogen
+ /*
this.map = map;
+ */
+ this.map = map instanceof Object2ObjectMap ? map : new Object2ObjectOpenHashMap<>(map);
+ // Yatopia end
}
public NBTTagCompound() {