ecf4d9715e
See diff in the update text file
13350 Zeilen
687 KiB
Diff
13350 Zeilen
687 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Mon, 21 Oct 2024 11:06:24 -0700
|
|
Subject: [PATCH] fixup! Moonrise optimisation patches
|
|
|
|
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java b/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java
|
|
index b61611351bf23efc1e90bab8a850ebbe6ffdd516..fc029c8fb22a7c8eeb23bfc171812f6da91c60fa 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java
|
|
@@ -6,11 +6,13 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
|
|
import com.mojang.logging.LogUtils;
|
|
import net.minecraft.server.level.ChunkHolder;
|
|
import net.minecraft.server.level.FullChunkStatus;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
+import net.minecraft.server.level.progress.ChunkProgressListener;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.LevelChunk;
|
|
@@ -80,7 +82,13 @@ public final class ChunkSystem {
|
|
}
|
|
|
|
public static void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
|
|
-
|
|
+ // Update progress listener for LevelLoadingScreen
|
|
+ final ChunkProgressListener progressListener = level.getChunkSource().chunkMap.progressListener;
|
|
+ if (progressListener != null) {
|
|
+ ChunkSystem.scheduleChunkTask(level, holder.getPos().x, holder.getPos().z, () -> {
|
|
+ progressListener.onStatusChange(holder.getPos(), null);
|
|
+ });
|
|
+ }
|
|
}
|
|
|
|
public static void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) {
|
|
@@ -112,16 +120,18 @@ public final class ChunkSystem {
|
|
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
|
);
|
|
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
|
|
- chunk.postProcessGeneration();
|
|
+ chunk.postProcessGeneration((ServerLevel)chunk.getLevel());
|
|
}
|
|
((ServerLevel)chunk.getLevel()).startTickingChunk(chunk);
|
|
((ServerLevel)chunk.getLevel()).getChunkSource().chunkMap.tickingGenerated.incrementAndGet();
|
|
+ ((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$markChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
|
|
}
|
|
|
|
public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
|
((ChunkSystemServerLevel)((ServerLevel)chunk.getLevel())).moonrise$getTickingChunks().remove(
|
|
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
|
|
);
|
|
+ ((ChunkTickServerLevel)(ServerLevel)chunk.getLevel()).moonrise$removeChunkForPlayerTicking(chunk); // Moonrise - chunk tick iteration
|
|
}
|
|
|
|
public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingBitStorage.java b/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingBitStorage.java
|
|
index aef4fc0d3c272febe675d1ac846b88e58b4e7533..93bc56daec4526f373c84763b8c7ccb4a30e800b 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingBitStorage.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingBitStorage.java
|
|
@@ -1,10 +1,10 @@
|
|
package ca.spottedleaf.moonrise.patches.block_counting;
|
|
|
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
-import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|
+import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
|
|
|
public interface BlockCountingBitStorage {
|
|
|
|
- public Int2ObjectOpenHashMap<IntArrayList> moonrise$countEntries();
|
|
+ public Int2ObjectOpenHashMap<ShortArrayList> moonrise$countEntries();
|
|
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingChunkSection.java b/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingChunkSection.java
|
|
index a08ddb0598d44368af5b6bace971ee31edf9919e..0d1443a113c07d7655e7b927a899447f70db8fa9 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingChunkSection.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingChunkSection.java
|
|
@@ -1,11 +1,11 @@
|
|
package ca.spottedleaf.moonrise.patches.block_counting;
|
|
|
|
-import ca.spottedleaf.moonrise.common.list.IBlockDataList;
|
|
+import ca.spottedleaf.moonrise.common.list.ShortList;
|
|
|
|
public interface BlockCountingChunkSection {
|
|
|
|
- public int moonrise$getSpecialCollidingBlocks();
|
|
+ public boolean moonrise$hasSpecialCollidingBlocks();
|
|
|
|
- public IBlockDataList moonrise$getTickingBlockList();
|
|
+ public ShortList moonrise$getTickingBlockList();
|
|
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccess.java b/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccess.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..89e75b454695e174c5619104eeb15eb923a2d9a7
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccess.java
|
|
@@ -0,0 +1,12 @@
|
|
+package ca.spottedleaf.moonrise.patches.blockstate_propertyaccess;
|
|
+
|
|
+public interface PropertyAccess<T> {
|
|
+
|
|
+ public int moonrise$getId();
|
|
+
|
|
+ public int moonrise$getIdFor(final T value);
|
|
+
|
|
+ public T moonrise$getById(final int id);
|
|
+
|
|
+ public void moonrise$setById(final T[] values);
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccessStateHolder.java b/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccessStateHolder.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..01da52b9e8a786824f199a057b62ce0431ecbc43
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccessStateHolder.java
|
|
@@ -0,0 +1,7 @@
|
|
+package ca.spottedleaf.moonrise.patches.blockstate_propertyaccess;
|
|
+
|
|
+public interface PropertyAccessStateHolder {
|
|
+
|
|
+ public long moonrise$getTableIndex();
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java b/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..b5335a2a8cb5dc7637c7112c8f7193389d726489
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java
|
|
@@ -0,0 +1,230 @@
|
|
+package ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.util;
|
|
+
|
|
+import ca.spottedleaf.concurrentutil.util.IntegerUtil;
|
|
+import ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccess;
|
|
+import ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccessStateHolder;
|
|
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
+import it.unimi.dsi.fastutil.objects.AbstractObjectSet;
|
|
+import it.unimi.dsi.fastutil.objects.AbstractReference2ObjectMap;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
|
+import it.unimi.dsi.fastutil.objects.ObjectSet;
|
|
+import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
|
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
|
+import java.util.ArrayList;
|
|
+import java.util.Collection;
|
|
+import java.util.Collections;
|
|
+import java.util.Iterator;
|
|
+import java.util.List;
|
|
+import java.util.Map;
|
|
+import net.minecraft.world.level.block.state.StateHolder;
|
|
+import net.minecraft.world.level.block.state.properties.Property;
|
|
+
|
|
+public final class ZeroCollidingReferenceStateTable<O, S> {
|
|
+
|
|
+ private final Int2ObjectOpenHashMap<Indexer> propertyToIndexer;
|
|
+ private S[] lookup;
|
|
+ private final Collection<Property<?>> properties;
|
|
+
|
|
+ public ZeroCollidingReferenceStateTable(final Collection<Property<?>> properties) {
|
|
+ this.propertyToIndexer = new Int2ObjectOpenHashMap<>(properties.size());
|
|
+ this.properties = new ReferenceOpenHashSet<>(properties);
|
|
+
|
|
+ final List<Property<?>> sortedProperties = new ArrayList<>(properties);
|
|
+
|
|
+ // important that each table sees the same property order given the same _set_ of properties,
|
|
+ // as each table will calculate the index for the block state
|
|
+ sortedProperties.sort((final Property<?> p1, final Property<?> p2) -> {
|
|
+ return Integer.compare(
|
|
+ ((PropertyAccess<?>)p1).moonrise$getId(),
|
|
+ ((PropertyAccess<?>)p2).moonrise$getId()
|
|
+ );
|
|
+ });
|
|
+
|
|
+ int currentMultiple = 1;
|
|
+ for (final Property<?> property : sortedProperties) {
|
|
+ final int totalValues = property.getPossibleValues().size();
|
|
+
|
|
+ this.propertyToIndexer.put(
|
|
+ ((PropertyAccess<?>)property).moonrise$getId(),
|
|
+ new Indexer(
|
|
+ totalValues,
|
|
+ currentMultiple,
|
|
+ IntegerUtil.getUnsignedDivisorMagic((long)currentMultiple, 32),
|
|
+ IntegerUtil.getUnsignedDivisorMagic((long)totalValues, 32)
|
|
+ )
|
|
+ );
|
|
+
|
|
+ currentMultiple *= totalValues;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public <T extends Comparable<T>> boolean hasProperty(final Property<T> property) {
|
|
+ return this.propertyToIndexer.containsKey(((PropertyAccess<T>)property).moonrise$getId());
|
|
+ }
|
|
+
|
|
+ public long getIndex(final StateHolder<O, S> stateHolder) {
|
|
+ long ret = 0L;
|
|
+
|
|
+ for (final Map.Entry<Property<?>, Comparable<?>> entry : stateHolder.getValues().entrySet()) {
|
|
+ final Property<?> property = entry.getKey();
|
|
+ final Comparable<?> value = entry.getValue();
|
|
+
|
|
+ final Indexer indexer = this.propertyToIndexer.get(((PropertyAccess<?>)property).moonrise$getId());
|
|
+
|
|
+ ret += (((PropertyAccess)property).moonrise$getIdFor(value)) * indexer.multiple;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ public boolean isLoaded() {
|
|
+ return this.lookup != null;
|
|
+ }
|
|
+
|
|
+ public void loadInTable(final Map<Map<Property<?>, Comparable<?>>, S> universe) {
|
|
+ if (this.lookup != null) {
|
|
+ throw new IllegalStateException();
|
|
+ }
|
|
+
|
|
+ this.lookup = (S[])new StateHolder[universe.size()];
|
|
+
|
|
+ for (final Map.Entry<Map<Property<?>, Comparable<?>>, S> entry : universe.entrySet()) {
|
|
+ final S value = entry.getValue();
|
|
+ if (value == null) {
|
|
+ continue;
|
|
+ }
|
|
+ this.lookup[(int)((PropertyAccessStateHolder)(StateHolder<O, S>)value).moonrise$getTableIndex()] = value;
|
|
+ }
|
|
+
|
|
+ for (final S value : this.lookup) {
|
|
+ if (value == null) {
|
|
+ throw new IllegalStateException();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public <T extends Comparable<T>> T get(final long index, final Property<T> property) {
|
|
+ final Indexer indexer = this.propertyToIndexer.get(((PropertyAccess<T>)property).moonrise$getId());
|
|
+ if (indexer == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ final long divided = (index * indexer.multipleDivMagic) >>> 32;
|
|
+ final long modded = (((divided * indexer.modMagic) & 0xFFFFFFFFL) * indexer.totalValues) >>> 32;
|
|
+ // equiv to: divided = index / multiple
|
|
+ // modded = divided % totalValues
|
|
+
|
|
+ return ((PropertyAccess<T>)property).moonrise$getById((int)modded);
|
|
+ }
|
|
+
|
|
+ public <T extends Comparable<T>> S set(final long index, final Property<T> property, final T with) {
|
|
+ final int newValueId = ((PropertyAccess<T>)property).moonrise$getIdFor(with);
|
|
+ if (newValueId < 0) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ final Indexer indexer = this.propertyToIndexer.get(((PropertyAccess<T>)property).moonrise$getId());
|
|
+ if (indexer == null) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ final long divided = (index * indexer.multipleDivMagic) >>> 32;
|
|
+ final long modded = (((divided * indexer.modMagic) & 0xFFFFFFFFL) * indexer.totalValues) >>> 32;
|
|
+ // equiv to: divided = index / multiple
|
|
+ // modded = divided % totalValues
|
|
+
|
|
+ // subtract out the old value, add in the new
|
|
+ final long newIndex = (((long)newValueId - modded) * indexer.multiple) + index;
|
|
+
|
|
+ return this.lookup[(int)newIndex];
|
|
+ }
|
|
+
|
|
+ public <T extends Comparable<T>> S trySet(final long index, final Property<T> property, final T with, final S dfl) {
|
|
+ final Indexer indexer = this.propertyToIndexer.get(((PropertyAccess<T>)property).moonrise$getId());
|
|
+ if (indexer == null) {
|
|
+ return dfl;
|
|
+ }
|
|
+
|
|
+ final int newValueId = ((PropertyAccess<T>)property).moonrise$getIdFor(with);
|
|
+ if (newValueId < 0) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ final long divided = (index * indexer.multipleDivMagic) >>> 32;
|
|
+ final long modded = (((divided * indexer.modMagic) & 0xFFFFFFFFL) * indexer.totalValues) >>> 32;
|
|
+ // equiv to: divided = index / multiple
|
|
+ // modded = divided % totalValues
|
|
+
|
|
+ // subtract out the old value, add in the new
|
|
+ final long newIndex = (((long)newValueId - modded) * indexer.multiple) + index;
|
|
+
|
|
+ return this.lookup[(int)newIndex];
|
|
+ }
|
|
+
|
|
+ public Collection<Property<?>> getProperties() {
|
|
+ return Collections.unmodifiableCollection(this.properties);
|
|
+ }
|
|
+
|
|
+ public Map<Property<?>, Comparable<?>> getMapView(final long stateIndex) {
|
|
+ return new MapView(stateIndex);
|
|
+ }
|
|
+
|
|
+ private static final record Indexer(
|
|
+ int totalValues, int multiple, long multipleDivMagic, long modMagic
|
|
+ ) {}
|
|
+
|
|
+ private class MapView extends AbstractReference2ObjectMap<Property<?>, Comparable<?>> {
|
|
+ private final long stateIndex;
|
|
+ private EntrySet entrySet;
|
|
+
|
|
+ MapView(final long stateIndex) {
|
|
+ this.stateIndex = stateIndex;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean containsKey(final Object key) {
|
|
+ return key instanceof Property<?> prop && ZeroCollidingReferenceStateTable.this.hasProperty(prop);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int size() {
|
|
+ return ZeroCollidingReferenceStateTable.this.properties.size();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ObjectSet<Entry<Property<?>, Comparable<?>>> reference2ObjectEntrySet() {
|
|
+ if (this.entrySet == null)
|
|
+ this.entrySet = new EntrySet();
|
|
+ return this.entrySet;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Comparable<?> get(final Object key) {
|
|
+ return key instanceof Property<?> prop ? ZeroCollidingReferenceStateTable.this.get(this.stateIndex, prop) : null;
|
|
+ }
|
|
+
|
|
+ class EntrySet extends AbstractObjectSet<Entry<Property<?>, Comparable<?>>> {
|
|
+ @Override
|
|
+ public ObjectIterator<Reference2ObjectMap.Entry<Property<?>, Comparable<?>>> iterator() {
|
|
+ final Iterator<Property<?>> propIterator = ZeroCollidingReferenceStateTable.this.properties.iterator();
|
|
+ return new ObjectIterator<>() {
|
|
+ @Override
|
|
+ public boolean hasNext() {
|
|
+ return propIterator.hasNext();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Entry<Property<?>, Comparable<?>> next() {
|
|
+ Property<?> prop = propIterator.next();
|
|
+ return new AbstractReference2ObjectMap.BasicEntry<>(prop, ZeroCollidingReferenceStateTable.this.get(MapView.this.stateIndex, prop));
|
|
+ }
|
|
+ };
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int size() {
|
|
+ return ZeroCollidingReferenceStateTable.this.properties.size();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemConverters.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemConverters.java
|
|
index 49160a30b8e19e5c5ada811fbcae2a05959524f3..44bb25554634af2ec0b2e9b3d9231304d5dff034 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemConverters.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemConverters.java
|
|
@@ -1,10 +1,11 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system;
|
|
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import net.minecraft.SharedConstants;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.Tag;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
-import net.minecraft.util.datafix.DataFixTypes;
|
|
+import net.minecraft.util.datafix.fixes.References;
|
|
|
|
public final class ChunkSystemConverters {
|
|
|
|
@@ -25,13 +26,13 @@ public final class ChunkSystemConverters {
|
|
public static CompoundTag convertPoiCompoundTag(final CompoundTag data, final ServerLevel world) {
|
|
final int dataVersion = getDataVersion(data, DEFAULT_POI_DATA_VERSION);
|
|
|
|
- return DataFixTypes.POI_CHUNK.update(world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
|
|
+ return PlatformHooks.get().convertNBT(References.POI_CHUNK, world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
|
|
}
|
|
|
|
public static CompoundTag convertEntityChunkCompoundTag(final CompoundTag data, final ServerLevel world) {
|
|
final int dataVersion = getDataVersion(data, DEFAULT_ENTITY_CHUNK_DATA_VERSION);
|
|
|
|
- return DataFixTypes.ENTITY_CHUNK.update(world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
|
|
+ return PlatformHooks.get().convertNBT(References.ENTITY_CHUNK, world.getServer().getFixerUpper(), data, dataVersion, getCurrentVersion());
|
|
}
|
|
|
|
private ChunkSystemConverters() {}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemFeatures.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemFeatures.java
|
|
deleted file mode 100644
|
|
index 67f6dd9a4855611cfe242c2e37e90f6d27d4c823..0000000000000000000000000000000000000000
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemFeatures.java
|
|
+++ /dev/null
|
|
@@ -1,36 +0,0 @@
|
|
-package ca.spottedleaf.moonrise.patches.chunk_system;
|
|
-
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.async_save.AsyncChunkSaveData;
|
|
-import net.minecraft.nbt.CompoundTag;
|
|
-import net.minecraft.server.level.ServerLevel;
|
|
-import net.minecraft.world.level.chunk.ChunkAccess;
|
|
-
|
|
-public final class ChunkSystemFeatures {
|
|
-
|
|
- public static boolean supportsAsyncChunkSave() {
|
|
- // uncertain how to properly pass AsyncSaveData to ChunkSerializer#write
|
|
- // additionally, there may be mods hooking into the write() call which may not be thread-safe to call
|
|
- return true;
|
|
- }
|
|
-
|
|
- public static AsyncChunkSaveData getAsyncSaveData(final ServerLevel world, final ChunkAccess chunk) {
|
|
- return net.minecraft.world.level.chunk.storage.ChunkSerializer.getAsyncSaveData(world, chunk);
|
|
- }
|
|
-
|
|
- public static CompoundTag saveChunkAsync(final ServerLevel world, final ChunkAccess chunk, final AsyncChunkSaveData asyncSaveData) {
|
|
- return net.minecraft.world.level.chunk.storage.ChunkSerializer.saveChunk(world, chunk, asyncSaveData);
|
|
- }
|
|
-
|
|
- public static boolean forceNoSave(final ChunkAccess chunk) {
|
|
- // support for CB chunk mustNotSave
|
|
- return chunk instanceof net.minecraft.world.level.chunk.LevelChunk levelChunk && levelChunk.mustNotSave;
|
|
- }
|
|
-
|
|
- public static boolean supportsAsyncChunkDeserialization() {
|
|
- // as it stands, the current problem with supporting this in Moonrise is that we are unsure that any mods
|
|
- // hooking into ChunkSerializer#read() are thread-safe to call
|
|
- return true;
|
|
- }
|
|
-
|
|
- private ChunkSystemFeatures() {}
|
|
-}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/async_save/AsyncChunkSaveData.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/async_save/AsyncChunkSaveData.java
|
|
deleted file mode 100644
|
|
index becd1c6d54ed6c912aee3a9178a970e2751d3694..0000000000000000000000000000000000000000
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/async_save/AsyncChunkSaveData.java
|
|
+++ /dev/null
|
|
@@ -1,11 +0,0 @@
|
|
-package ca.spottedleaf.moonrise.patches.chunk_system.async_save;
|
|
-
|
|
-import net.minecraft.nbt.ListTag;
|
|
-import net.minecraft.nbt.Tag;
|
|
-
|
|
-public record AsyncChunkSaveData(
|
|
- Tag blockTickList, // non-null if we had to go to the server's tick list
|
|
- Tag fluidTickList, // non-null if we had to go to the server's tick list
|
|
- ListTag blockEntities,
|
|
- long worldTime
|
|
-) {}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/entity/ChunkSystemEntity.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/entity/ChunkSystemEntity.java
|
|
index 2c279854bdf214538380fa354e4298ec4bd9ac4e..c7da23900228aab3a5673eb5adfada5091140319 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/entity/ChunkSystemEntity.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/entity/ChunkSystemEntity.java
|
|
@@ -1,5 +1,6 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.entity;
|
|
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
|
import net.minecraft.server.level.FullChunkStatus;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.monster.Shulker;
|
|
@@ -19,6 +20,10 @@ public interface ChunkSystemEntity {
|
|
|
|
public void moonrise$setChunkStatus(final FullChunkStatus status);
|
|
|
|
+ public ChunkData moonrise$getChunkData();
|
|
+
|
|
+ public void moonrise$setChunkData(final ChunkData chunkData);
|
|
+
|
|
public int moonrise$getSectionX();
|
|
|
|
public void moonrise$setSectionX(final int x);
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java
|
|
index 73df26b27146bbad2106d57b22dd3c792ed3dd1d..a814512fcfb85312474ae2c2c21443843bf57831 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java
|
|
@@ -1,5 +1,6 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.io;
|
|
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.world.level.chunk.storage.RegionFile;
|
|
import java.io.IOException;
|
|
|
|
@@ -11,4 +12,20 @@ public interface ChunkSystemRegionFileStorage {
|
|
|
|
public RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException;
|
|
|
|
+ public MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite(
|
|
+ final int chunkX, final int chunkZ, final CompoundTag compound
|
|
+ ) throws IOException;
|
|
+
|
|
+ public void moonrise$finishWrite(
|
|
+ final int chunkX, final int chunkZ, final MoonriseRegionFileIO.RegionDataController.WriteData writeData
|
|
+ ) throws IOException;
|
|
+
|
|
+ public MoonriseRegionFileIO.RegionDataController.ReadData moonrise$readData(
|
|
+ final int chunkX, final int chunkZ
|
|
+ ) throws IOException;
|
|
+
|
|
+ // if the return value is null, then the caller needs to re-try with a new call to readData()
|
|
+ public CompoundTag moonrise$finishRead(
|
|
+ final int chunkX, final int chunkZ, final MoonriseRegionFileIO.RegionDataController.ReadData readData
|
|
+ ) throws IOException;
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..99f6f3e58b11b8967e6f1c3391c190d9a860ab7f
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
|
|
@@ -0,0 +1,1707 @@
|
|
+package ca.spottedleaf.moonrise.patches.chunk_system.io;
|
|
+
|
|
+import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
|
+import ca.spottedleaf.concurrentutil.completable.CallbackCompletable;
|
|
+import ca.spottedleaf.concurrentutil.completable.Completable;
|
|
+import ca.spottedleaf.concurrentutil.executor.Cancellable;
|
|
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.executor.queue.PrioritisedTaskQueue;
|
|
+import ca.spottedleaf.concurrentutil.function.BiLong1Function;
|
|
+import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
|
+import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
+import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
+import ca.spottedleaf.moonrise.common.util.TickThread;
|
|
+import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
|
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.world.level.chunk.storage.RegionFile;
|
|
+import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
|
+import org.slf4j.Logger;
|
|
+import org.slf4j.LoggerFactory;
|
|
+import java.io.DataInputStream;
|
|
+import java.io.DataOutputStream;
|
|
+import java.io.IOException;
|
|
+import java.lang.invoke.VarHandle;
|
|
+import java.util.concurrent.CompletableFuture;
|
|
+import java.util.concurrent.CompletionException;
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
+import java.util.concurrent.atomic.AtomicLong;
|
|
+import java.util.function.BiConsumer;
|
|
+import java.util.function.Consumer;
|
|
+
|
|
+public final class MoonriseRegionFileIO {
|
|
+
|
|
+ private static final int REGION_FILE_SHIFT = 5;
|
|
+ private static final Logger LOGGER = LoggerFactory.getLogger(MoonriseRegionFileIO.class);
|
|
+
|
|
+ /**
|
|
+ * The types of RegionFiles controlled by the I/O thread(s).
|
|
+ */
|
|
+ public static enum RegionFileType {
|
|
+ CHUNK_DATA,
|
|
+ POI_DATA,
|
|
+ ENTITY_DATA;
|
|
+ }
|
|
+
|
|
+ public static RegionDataController getControllerFor(final ServerLevel world, final RegionFileType type) {
|
|
+ switch (type) {
|
|
+ case CHUNK_DATA:
|
|
+ return ((ChunkSystemServerLevel)world).moonrise$getChunkDataController();
|
|
+ case POI_DATA:
|
|
+ return ((ChunkSystemServerLevel)world).moonrise$getPoiChunkDataController();
|
|
+ case ENTITY_DATA:
|
|
+ return ((ChunkSystemServerLevel)world).moonrise$getEntityChunkDataController();
|
|
+ default:
|
|
+ throw new IllegalStateException("Unknown controller type " + type);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final RegionFileType[] CACHED_REGIONFILE_TYPES = RegionFileType.values();
|
|
+
|
|
+ /**
|
|
+ * Collects RegionFile data for a certain chunk.
|
|
+ */
|
|
+ public static final class RegionFileData {
|
|
+
|
|
+ private final boolean[] hasResult = new boolean[CACHED_REGIONFILE_TYPES.length];
|
|
+ private final CompoundTag[] data = new CompoundTag[CACHED_REGIONFILE_TYPES.length];
|
|
+ private final Throwable[] throwables = new Throwable[CACHED_REGIONFILE_TYPES.length];
|
|
+
|
|
+ /**
|
|
+ * Sets the result associated with the specified RegionFile type. Note that
|
|
+ * results can only be set once per RegionFile type.
|
|
+ *
|
|
+ * @param type The RegionFile type.
|
|
+ * @param data The result to set.
|
|
+ */
|
|
+ public void setData(final MoonriseRegionFileIO.RegionFileType type, final CompoundTag data) {
|
|
+ final int index = type.ordinal();
|
|
+
|
|
+ if (this.hasResult[index]) {
|
|
+ throw new IllegalArgumentException("Result already exists for type " + type);
|
|
+ }
|
|
+ this.hasResult[index] = true;
|
|
+ this.data[index] = data;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Sets the result associated with the specified RegionFile type. Note that
|
|
+ * results can only be set once per RegionFile type.
|
|
+ *
|
|
+ * @param type The RegionFile type.
|
|
+ * @param throwable The result to set.
|
|
+ */
|
|
+ public void setThrowable(final MoonriseRegionFileIO.RegionFileType type, final Throwable throwable) {
|
|
+ final int index = type.ordinal();
|
|
+
|
|
+ if (this.hasResult[index]) {
|
|
+ throw new IllegalArgumentException("Result already exists for type " + type);
|
|
+ }
|
|
+ this.hasResult[index] = true;
|
|
+ this.throwables[index] = throwable;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Returns whether there is a result for the specified RegionFile type.
|
|
+ *
|
|
+ * @param type Specified RegionFile type.
|
|
+ *
|
|
+ * @return Whether a result exists for {@code type}.
|
|
+ */
|
|
+ public boolean hasResult(final MoonriseRegionFileIO.RegionFileType type) {
|
|
+ return this.hasResult[type.ordinal()];
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Returns the data result for the RegionFile type.
|
|
+ *
|
|
+ * @param type Specified RegionFile type.
|
|
+ *
|
|
+ * @throws IllegalArgumentException If the result has not been set for {@code type}.
|
|
+ * @return The data result for the specified type. If the result is a {@code Throwable},
|
|
+ * then returns {@code null}.
|
|
+ */
|
|
+ public CompoundTag getData(final MoonriseRegionFileIO.RegionFileType type) {
|
|
+ final int index = type.ordinal();
|
|
+
|
|
+ if (!this.hasResult[index]) {
|
|
+ throw new IllegalArgumentException("Result does not exist for type " + type);
|
|
+ }
|
|
+
|
|
+ return this.data[index];
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Returns the throwable result for the RegionFile type.
|
|
+ *
|
|
+ * @param type Specified RegionFile type.
|
|
+ *
|
|
+ * @throws IllegalArgumentException If the result has not been set for {@code type}.
|
|
+ * @return The throwable result for the specified type. If the result is an {@code CompoundTag},
|
|
+ * then returns {@code null}.
|
|
+ */
|
|
+ public Throwable getThrowable(final MoonriseRegionFileIO.RegionFileType type) {
|
|
+ final int index = type.ordinal();
|
|
+
|
|
+ if (!this.hasResult[index]) {
|
|
+ throw new IllegalArgumentException("Result does not exist for type " + type);
|
|
+ }
|
|
+
|
|
+ return this.throwables[index];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void flushRegionStorages(final ServerLevel world) throws IOException {
|
|
+ for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
|
|
+ flushRegionStorages(world, type);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void flushRegionStorages(final ServerLevel world, final RegionFileType type) throws IOException {
|
|
+ getControllerFor(world, type).getCache().flush();
|
|
+ }
|
|
+
|
|
+ public static void flush(final MinecraftServer server) {
|
|
+ for (final ServerLevel world : server.getAllLevels()) {
|
|
+ flush(world);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void flush(final ServerLevel world) {
|
|
+ for (final RegionFileType regionFileType : CACHED_REGIONFILE_TYPES) {
|
|
+ flush(world, regionFileType);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void flush(final ServerLevel world, final RegionFileType type) {
|
|
+ final RegionDataController taskController = getControllerFor(world, type);
|
|
+
|
|
+ long failures = 1L; // start at 0.13ms
|
|
+
|
|
+ while (taskController.hasTasks()) {
|
|
+ Thread.yield();
|
|
+ failures = ConcurrentUtil.linearLongBackoff(failures, 125_000L, 5_000_000L); // 125us, 5ms
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static void partialFlush(final ServerLevel world, final int tasksRemaining) {
|
|
+ for (long failures = 1L;;) { // start at 0.13ms
|
|
+ long totalTasks = 0L;
|
|
+ for (final RegionFileType regionFileType : CACHED_REGIONFILE_TYPES) {
|
|
+ totalTasks += getControllerFor(world, regionFileType).getTotalWorkingTasks();
|
|
+ }
|
|
+
|
|
+ if (totalTasks > (long)tasksRemaining) {
|
|
+ Thread.yield();
|
|
+ failures = ConcurrentUtil.linearLongBackoff(failures, 125_000L, 5_000_000L); // 125us, 5ms
|
|
+ } else {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Returns the priority associated with blocking I/O based on the current thread. The goal is to avoid
|
|
+ * dumb plugins from taking away priority from threads we consider crucial.
|
|
+ * @return The priroity to use with blocking I/O on the current thread.
|
|
+ */
|
|
+ public static Priority getIOBlockingPriorityForCurrentThread() {
|
|
+ if (TickThread.isTickThread()) {
|
|
+ return Priority.BLOCKING;
|
|
+ }
|
|
+ return Priority.HIGHEST;
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Returns the priority for the specified regionfile type for the specified chunk.
|
|
+ * @param world Specified world.
|
|
+ * @param chunkX Specified chunk x.
|
|
+ * @param chunkZ Specified chunk z.
|
|
+ * @param type Specified regionfile type.
|
|
+ * @return The priority for the chunk
|
|
+ */
|
|
+ public static Priority getPriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
|
|
+ final RegionDataController taskController = getControllerFor(world, type);
|
|
+ final ChunkIOTask task = taskController.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
+
|
|
+ if (task == null) {
|
|
+ return Priority.COMPLETING;
|
|
+ }
|
|
+
|
|
+ return task.getPriority();
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Sets the priority for all regionfile types for the specified chunk. Note that great care should
|
|
+ * be taken using this method, as there can be multiple tasks tied to the same chunk that want different
|
|
+ * priorities.
|
|
+ *
|
|
+ * @param world Specified world.
|
|
+ * @param chunkX Specified chunk x.
|
|
+ * @param chunkZ Specified chunk z.
|
|
+ * @param priority New priority.
|
|
+ *
|
|
+ * @see #raisePriority(ServerLevel, int, int, Priority)
|
|
+ * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ * @see #lowerPriority(ServerLevel, int, int, Priority)
|
|
+ * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ */
|
|
+ public static void setPriority(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
+ final Priority priority) {
|
|
+ for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
|
|
+ MoonriseRegionFileIO.setPriority(world, chunkX, chunkZ, type, priority);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Sets the priority for the specified regionfile type for the specified chunk. Note that great care should
|
|
+ * be taken using this method, as there can be multiple tasks tied to the same chunk that want different
|
|
+ * priorities.
|
|
+ *
|
|
+ * @param world Specified world.
|
|
+ * @param chunkX Specified chunk x.
|
|
+ * @param chunkZ Specified chunk z.
|
|
+ * @param type Specified regionfile type.
|
|
+ * @param priority New priority.
|
|
+ *
|
|
+ * @see #raisePriority(ServerLevel, int, int, Priority)
|
|
+ * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ * @see #lowerPriority(ServerLevel, int, int, Priority)
|
|
+ * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ */
|
|
+ public static void setPriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
+ final Priority priority) {
|
|
+ final RegionDataController taskController = getControllerFor(world, type);
|
|
+ final ChunkIOTask task = taskController.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
+
|
|
+ if (task != null) {
|
|
+ task.setPriority(priority);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Raises the priority for all regionfile types for the specified chunk.
|
|
+ *
|
|
+ * @param world Specified world.
|
|
+ * @param chunkX Specified chunk x.
|
|
+ * @param chunkZ Specified chunk z.
|
|
+ * @param priority New priority.
|
|
+ *
|
|
+ * @see #setPriority(ServerLevel, int, int, Priority)
|
|
+ * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ * @see #lowerPriority(ServerLevel, int, int, Priority)
|
|
+ * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ */
|
|
+ public static void raisePriority(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
+ final Priority priority) {
|
|
+ for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
|
|
+ MoonriseRegionFileIO.raisePriority(world, chunkX, chunkZ, type, priority);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Raises the priority for the specified regionfile type for the specified chunk.
|
|
+ *
|
|
+ * @param world Specified world.
|
|
+ * @param chunkX Specified chunk x.
|
|
+ * @param chunkZ Specified chunk z.
|
|
+ * @param type Specified regionfile type.
|
|
+ * @param priority New priority.
|
|
+ *
|
|
+ * @see #setPriority(ServerLevel, int, int, Priority)
|
|
+ * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ * @see #lowerPriority(ServerLevel, int, int, Priority)
|
|
+ * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ */
|
|
+ public static void raisePriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
+ final Priority priority) {
|
|
+ final RegionDataController taskController = getControllerFor(world, type);
|
|
+ final ChunkIOTask task = taskController.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
+
|
|
+ if (task != null) {
|
|
+ task.raisePriority(priority);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Lowers the priority for all regionfile types for the specified chunk.
|
|
+ *
|
|
+ * @param world Specified world.
|
|
+ * @param chunkX Specified chunk x.
|
|
+ * @param chunkZ Specified chunk z.
|
|
+ * @param priority New priority.
|
|
+ *
|
|
+ * @see #raisePriority(ServerLevel, int, int, Priority)
|
|
+ * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ * @see #setPriority(ServerLevel, int, int, Priority)
|
|
+ * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ */
|
|
+ public static void lowerPriority(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
+ final Priority priority) {
|
|
+ for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
|
|
+ MoonriseRegionFileIO.lowerPriority(world, chunkX, chunkZ, type, priority);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Lowers the priority for the specified regionfile type for the specified chunk.
|
|
+ *
|
|
+ * @param world Specified world.
|
|
+ * @param chunkX Specified chunk x.
|
|
+ * @param chunkZ Specified chunk z.
|
|
+ * @param type Specified regionfile type.
|
|
+ * @param priority New priority.
|
|
+ *
|
|
+ * @see #raisePriority(ServerLevel, int, int, Priority)
|
|
+ * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ * @see #setPriority(ServerLevel, int, int, Priority)
|
|
+ * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
+ */
|
|
+ public static void lowerPriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
+ final Priority priority) {
|
|
+ final RegionDataController taskController = getControllerFor(world, type);
|
|
+ final ChunkIOTask task = taskController.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
+
|
|
+ if (task != null) {
|
|
+ task.lowerPriority(priority);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules the chunk data to be written asynchronously.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
|
|
+ * saves must be scheduled before a chunk is unloaded.
|
|
+ * </li>
|
|
+ * <li>
|
|
+ * Writes may be called concurrently, although only the "later" write will go through.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param data Chunk's data
|
|
+ * @param type The regionfile type to write to.
|
|
+ *
|
|
+ * @throws IllegalStateException If the file io thread has shutdown.
|
|
+ */
|
|
+ public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
|
|
+ final RegionFileType type) {
|
|
+ MoonriseRegionFileIO.scheduleSave(world, chunkX, chunkZ, data, type, Priority.NORMAL);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules the chunk data to be written asynchronously.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
|
|
+ * saves must be scheduled before a chunk is unloaded.
|
|
+ * </li>
|
|
+ * <li>
|
|
+ * Writes may be called concurrently, although only the "later" write will go through.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param data Chunk's data
|
|
+ * @param type The regionfile type to write to.
|
|
+ * @param priority The minimum priority to schedule at.
|
|
+ *
|
|
+ * @throws IllegalStateException If the file io thread has shutdown.
|
|
+ */
|
|
+ public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
|
|
+ final RegionFileType type, final Priority priority) {
|
|
+ scheduleSave(
|
|
+ world, chunkX, chunkZ,
|
|
+ (final BiConsumer<CompoundTag, Throwable> consumer) -> {
|
|
+ consumer.accept(data, null);
|
|
+ }, null, type, priority
|
|
+ );
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules the chunk data to be written asynchronously.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
|
|
+ * saves must be scheduled before a chunk is unloaded.
|
|
+ * </li>
|
|
+ * <li>
|
|
+ * Writes may be called concurrently, although only the "later" write will go through.
|
|
+ * </li>
|
|
+ * <li>
|
|
+ * The specified write task, if not null, will have its priority controlled by the scheduler.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param completable Chunk's pending data
|
|
+ * @param writeTask The task responsible for completing the pending chunk data
|
|
+ * @param type The regionfile type to write to.
|
|
+ * @param priority The minimum priority to schedule at.
|
|
+ *
|
|
+ * @throws IllegalStateException If the file io thread has shutdown.
|
|
+ */
|
|
+ public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CallbackCompletable<CompoundTag> completable,
|
|
+ final PrioritisedExecutor.PrioritisedTask writeTask, final RegionFileType type, final Priority priority) {
|
|
+ scheduleSave(world, chunkX, chunkZ, completable::addWaiter, writeTask, type, priority);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules the chunk data to be written asynchronously.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
|
|
+ * saves must be scheduled before a chunk is unloaded.
|
|
+ * </li>
|
|
+ * <li>
|
|
+ * Writes may be called concurrently, although only the "later" write will go through.
|
|
+ * </li>
|
|
+ * <li>
|
|
+ * The specified write task, if not null, will have its priority controlled by the scheduler.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param completable Chunk's pending data
|
|
+ * @param writeTask The task responsible for completing the pending chunk data
|
|
+ * @param type The regionfile type to write to.
|
|
+ * @param priority The minimum priority to schedule at.
|
|
+ *
|
|
+ * @throws IllegalStateException If the file io thread has shutdown.
|
|
+ */
|
|
+ public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final Completable<CompoundTag> completable,
|
|
+ final PrioritisedExecutor.PrioritisedTask writeTask, final RegionFileType type, final Priority priority) {
|
|
+ scheduleSave(world, chunkX, chunkZ, completable::whenComplete, writeTask, type, priority);
|
|
+ }
|
|
+
|
|
+ private static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final Consumer<BiConsumer<CompoundTag, Throwable>> scheduler,
|
|
+ final PrioritisedExecutor.PrioritisedTask writeTask, final RegionFileType type, final Priority priority) {
|
|
+ final RegionDataController taskController = getControllerFor(world, type);
|
|
+
|
|
+ final boolean[] created = new boolean[1];
|
|
+ final ChunkIOTask.InProgressWrite write = new ChunkIOTask.InProgressWrite(writeTask);
|
|
+ final ChunkIOTask task = taskController.chunkTasks.compute(CoordinateUtils.getChunkKey(chunkX, chunkZ),
|
|
+ (final long keyInMap, final ChunkIOTask taskRunning) -> {
|
|
+ if (taskRunning == null || taskRunning.failedWrite) {
|
|
+ // no task is scheduled or the previous write failed - meaning we need to overwrite it
|
|
+
|
|
+ // create task
|
|
+ final ChunkIOTask newTask = new ChunkIOTask(
|
|
+ world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead()
|
|
+ );
|
|
+
|
|
+ newTask.pushPendingWrite(write);
|
|
+
|
|
+ created[0] = true;
|
|
+
|
|
+ return newTask;
|
|
+ }
|
|
+
|
|
+ taskRunning.pushPendingWrite(write);
|
|
+
|
|
+ return taskRunning;
|
|
+ }
|
|
+ );
|
|
+
|
|
+ write.schedule(task, scheduler);
|
|
+
|
|
+ if (created[0]) {
|
|
+ taskController.startTask(task);
|
|
+ task.scheduleWriteCompress();
|
|
+ } else {
|
|
+ task.raisePriority(priority);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules a load to be executed asynchronously. This task will load all regionfile types, and then call
|
|
+ * {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)}
|
|
+ * for single load.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
+ * data is undefined behaviour, and can cause deadlock.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param onComplete Consumer to execute once this task has completed
|
|
+ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
+ * of this call.
|
|
+ *
|
|
+ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
+ *
|
|
+ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
|
|
+ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
|
|
+ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
|
|
+ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
|
|
+ */
|
|
+ public static Cancellable loadAllChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
+ final Consumer<RegionFileData> onComplete, final boolean intendingToBlock) {
|
|
+ return MoonriseRegionFileIO.loadAllChunkData(world, chunkX, chunkZ, onComplete, intendingToBlock, Priority.NORMAL);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules a load to be executed asynchronously. This task will load all regionfile types, and then call
|
|
+ * {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)}
|
|
+ * for single load.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
+ * data is undefined behaviour, and can cause deadlock.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param onComplete Consumer to execute once this task has completed
|
|
+ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
+ * of this call.
|
|
+ * @param priority The minimum priority to load the data at.
|
|
+ *
|
|
+ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
+ *
|
|
+ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
|
|
+ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
|
|
+ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
|
|
+ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
|
|
+ */
|
|
+ public static Cancellable loadAllChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
+ final Consumer<RegionFileData> onComplete, final boolean intendingToBlock,
|
|
+ final Priority priority) {
|
|
+ return MoonriseRegionFileIO.loadChunkData(world, chunkX, chunkZ, onComplete, intendingToBlock, priority, CACHED_REGIONFILE_TYPES);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules a load to be executed asynchronously. This task will load data for the specified regionfile type(s), and
|
|
+ * then call {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)}
|
|
+ * for single load.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
+ * data is undefined behaviour, and can cause deadlock.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param onComplete Consumer to execute once this task has completed
|
|
+ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
+ * of this call.
|
|
+ * @param types The regionfile type(s) to load.
|
|
+ *
|
|
+ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
+ *
|
|
+ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
|
|
+ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
|
|
+ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
|
|
+ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
|
|
+ */
|
|
+ public static Cancellable loadChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
+ final Consumer<RegionFileData> onComplete, final boolean intendingToBlock,
|
|
+ final RegionFileType... types) {
|
|
+ return MoonriseRegionFileIO.loadChunkData(world, chunkX, chunkZ, onComplete, intendingToBlock, Priority.NORMAL, types);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules a load to be executed asynchronously. This task will load data for the specified regionfile type(s), and
|
|
+ * then call {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)}
|
|
+ * for single load.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
+ * data is undefined behaviour, and can cause deadlock.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param onComplete Consumer to execute once this task has completed
|
|
+ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
+ * of this call.
|
|
+ * @param types The regionfile type(s) to load.
|
|
+ * @param priority The minimum priority to load the data at.
|
|
+ *
|
|
+ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
+ *
|
|
+ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
|
|
+ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
|
|
+ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
|
|
+ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
|
|
+ */
|
|
+ public static Cancellable loadChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
+ final Consumer<RegionFileData> onComplete, final boolean intendingToBlock,
|
|
+ final Priority priority, final RegionFileType... types) {
|
|
+ if (types == null) {
|
|
+ throw new NullPointerException("Types cannot be null");
|
|
+ }
|
|
+ if (types.length == 0) {
|
|
+ throw new IllegalArgumentException("Types cannot be empty");
|
|
+ }
|
|
+
|
|
+ final RegionFileData ret = new RegionFileData();
|
|
+
|
|
+ final Cancellable[] reads = new CancellableRead[types.length];
|
|
+ final AtomicInteger completions = new AtomicInteger();
|
|
+ final int expectedCompletions = types.length;
|
|
+
|
|
+ for (int i = 0; i < expectedCompletions; ++i) {
|
|
+ final RegionFileType type = types[i];
|
|
+ reads[i] = MoonriseRegionFileIO.loadDataAsync(world, chunkX, chunkZ, type,
|
|
+ (final CompoundTag data, final Throwable throwable) -> {
|
|
+ if (throwable != null) {
|
|
+ ret.setThrowable(type, throwable);
|
|
+ } else {
|
|
+ ret.setData(type, data);
|
|
+ }
|
|
+
|
|
+ if (completions.incrementAndGet() == expectedCompletions) {
|
|
+ onComplete.accept(ret);
|
|
+ }
|
|
+ }, intendingToBlock, priority);
|
|
+ }
|
|
+
|
|
+ return new CancellableReads(reads);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules a load to be executed asynchronously. This task will load the specified regionfile type, and then call
|
|
+ * {@code onComplete}.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
+ * data is undefined behaviour, and can cause deadlock.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param onComplete Consumer to execute once this task has completed
|
|
+ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
+ * of this call.
|
|
+ *
|
|
+ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
+ *
|
|
+ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
|
|
+ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
|
|
+ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
|
|
+ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
|
|
+ */
|
|
+ public static Cancellable loadDataAsync(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
+ final RegionFileType type, final BiConsumer<CompoundTag, Throwable> onComplete,
|
|
+ final boolean intendingToBlock) {
|
|
+ return MoonriseRegionFileIO.loadDataAsync(world, chunkX, chunkZ, type, onComplete, intendingToBlock, Priority.NORMAL);
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules a load to be executed asynchronously. This task will load the specified regionfile type, and then call
|
|
+ * {@code onComplete}.
|
|
+ * <p>
|
|
+ * Impl notes:
|
|
+ * </p>
|
|
+ * <li>
|
|
+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
+ * data is undefined behaviour, and can cause deadlock.
|
|
+ * </li>
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param onComplete Consumer to execute once this task has completed
|
|
+ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
+ * of this call.
|
|
+ * @param priority Minimum priority to load the data at.
|
|
+ *
|
|
+ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
+ *
|
|
+ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
|
|
+ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
|
|
+ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
|
|
+ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
|
|
+ */
|
|
+ public static Cancellable loadDataAsync(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
+ final RegionFileType type, final BiConsumer<CompoundTag, Throwable> onComplete,
|
|
+ final boolean intendingToBlock, final Priority priority) {
|
|
+ final RegionDataController taskController = getControllerFor(world, type);
|
|
+
|
|
+ final ImmediateCallbackCompletion callbackInfo = new ImmediateCallbackCompletion();
|
|
+
|
|
+ final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
|
+ final BiLong1Function<ChunkIOTask, ChunkIOTask> compute = (final long keyInMap, final ChunkIOTask running) -> {
|
|
+ if (running == null) {
|
|
+ // not scheduled
|
|
+
|
|
+ // set up task
|
|
+ final ChunkIOTask newTask = new ChunkIOTask(
|
|
+ world, taskController, chunkX, chunkZ, priority, new ChunkIOTask.InProgressRead()
|
|
+ );
|
|
+ newTask.inProgressRead.addToAsyncWaiters(onComplete);
|
|
+
|
|
+ callbackInfo.tasksNeedReadScheduling = true;
|
|
+ return newTask;
|
|
+ }
|
|
+
|
|
+ final ChunkIOTask.InProgressWrite pendingWrite = running.inProgressWrite;
|
|
+
|
|
+ if (pendingWrite == null) {
|
|
+ // need to add to waiters here, because the regionfile thread will use compute() to lock and check for cancellations
|
|
+ if (!running.inProgressRead.addToAsyncWaiters(onComplete)) {
|
|
+ callbackInfo.data = running.inProgressRead.value;
|
|
+ callbackInfo.throwable = running.inProgressRead.throwable;
|
|
+ callbackInfo.completeNow = true;
|
|
+ return running;
|
|
+ }
|
|
+
|
|
+ callbackInfo.read = running.inProgressRead;
|
|
+
|
|
+ return running;
|
|
+ }
|
|
+
|
|
+ // at this stage we have to use the in progress write's data to avoid an order issue
|
|
+
|
|
+ if (!pendingWrite.addToAsyncWaiters(onComplete)) {
|
|
+ // data is ready now
|
|
+ callbackInfo.data = pendingWrite.value;
|
|
+ callbackInfo.throwable = pendingWrite.throwable;
|
|
+ callbackInfo.completeNow = true;
|
|
+ return running;
|
|
+ }
|
|
+
|
|
+ callbackInfo.write = pendingWrite;
|
|
+
|
|
+ return running;
|
|
+ };
|
|
+
|
|
+ final ChunkIOTask ret = taskController.chunkTasks.compute(key, compute);
|
|
+
|
|
+ // needs to be scheduled
|
|
+ if (callbackInfo.tasksNeedReadScheduling) {
|
|
+ taskController.startTask(ret);
|
|
+ ret.scheduleReadIO();
|
|
+ } else if (callbackInfo.completeNow) {
|
|
+ try {
|
|
+ onComplete.accept(callbackInfo.data == null ? null : callbackInfo.data.copy(), callbackInfo.throwable);
|
|
+ } catch (final Throwable thr) {
|
|
+ LOGGER.error("Callback " + ConcurrentUtil.genericToString(onComplete) + " synchronously failed to handle chunk data for task " + ret.toString(), thr);
|
|
+ }
|
|
+ } else {
|
|
+ // we're waiting on a task we didn't schedule, so raise its priority to what we want
|
|
+ ret.raisePriority(priority);
|
|
+ }
|
|
+
|
|
+ return new CancellableRead(onComplete, callbackInfo.read, callbackInfo.write);
|
|
+ }
|
|
+
|
|
+ private static final class ImmediateCallbackCompletion {
|
|
+
|
|
+ private CompoundTag data;
|
|
+ private Throwable throwable;
|
|
+ private boolean completeNow;
|
|
+ private boolean tasksNeedReadScheduling;
|
|
+ private ChunkIOTask.InProgressRead read;
|
|
+ private ChunkIOTask.InProgressWrite write;
|
|
+
|
|
+ }
|
|
+
|
|
+ /**
|
|
+ * Schedules a load task to be executed asynchronously, and blocks on that task.
|
|
+ *
|
|
+ * @param world Chunk's world
|
|
+ * @param chunkX Chunk's x coordinate
|
|
+ * @param chunkZ Chunk's z coordinate
|
|
+ * @param type Regionfile type
|
|
+ * @param priority Minimum priority to load the data at.
|
|
+ *
|
|
+ * @return The chunk data for the chunk. Note that a {@code null} result means the chunk or regionfile does not exist on disk.
|
|
+ *
|
|
+ * @throws IOException If the load fails for any reason
|
|
+ */
|
|
+ public static CompoundTag loadData(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
+ final Priority priority) throws IOException {
|
|
+ final CompletableFuture<CompoundTag> ret = new CompletableFuture<>();
|
|
+
|
|
+ MoonriseRegionFileIO.loadDataAsync(world, chunkX, chunkZ, type, (final CompoundTag compound, final Throwable thr) -> {
|
|
+ if (thr != null) {
|
|
+ ret.completeExceptionally(thr);
|
|
+ } else {
|
|
+ ret.complete(compound);
|
|
+ }
|
|
+ }, true, priority);
|
|
+
|
|
+ try {
|
|
+ return ret.join();
|
|
+ } catch (final CompletionException ex) {
|
|
+ throw new IOException(ex);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final class CancellableRead implements Cancellable {
|
|
+
|
|
+ private BiConsumer<CompoundTag, Throwable> callback;
|
|
+ private ChunkIOTask.InProgressRead read;
|
|
+ private ChunkIOTask.InProgressWrite write;
|
|
+
|
|
+ private CancellableRead(final BiConsumer<CompoundTag, Throwable> callback,
|
|
+ final ChunkIOTask.InProgressRead read,
|
|
+ final ChunkIOTask.InProgressWrite write) {
|
|
+ this.callback = callback;
|
|
+ this.read = read;
|
|
+ this.write = write;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean cancel() {
|
|
+ final BiConsumer<CompoundTag, Throwable> callback = this.callback;
|
|
+ final ChunkIOTask.InProgressRead read = this.read;
|
|
+ final ChunkIOTask.InProgressWrite write = this.write;
|
|
+
|
|
+ if (callback == null || (read == null && write == null)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ this.callback = null;
|
|
+ this.read = null;
|
|
+ this.write = null;
|
|
+
|
|
+ if (read != null) {
|
|
+ return read.cancel(callback);
|
|
+ }
|
|
+ if (write != null) {
|
|
+ return write.cancel(callback);
|
|
+ }
|
|
+
|
|
+ // unreachable
|
|
+ throw new InternalError();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final class CancellableReads implements Cancellable {
|
|
+
|
|
+ private Cancellable[] reads;
|
|
+ private static final VarHandle READS_HANDLE = ConcurrentUtil.getVarHandle(CancellableReads.class, "reads", Cancellable[].class);
|
|
+
|
|
+ private CancellableReads(final Cancellable[] reads) {
|
|
+ this.reads = reads;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean cancel() {
|
|
+ final Cancellable[] reads = (Cancellable[])READS_HANDLE.getAndSet((CancellableReads)this, (Cancellable[])null);
|
|
+
|
|
+ if (reads == null) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ boolean ret = false;
|
|
+
|
|
+ for (final Cancellable read : reads) {
|
|
+ ret |= read.cancel();
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final class ChunkIOTask {
|
|
+
|
|
+ private final ServerLevel world;
|
|
+ private final RegionDataController regionDataController;
|
|
+ private final int chunkX;
|
|
+ private final int chunkZ;
|
|
+ private Priority priority;
|
|
+ private PrioritisedExecutor.PrioritisedTask currentTask;
|
|
+
|
|
+ private final InProgressRead inProgressRead;
|
|
+ private volatile InProgressWrite inProgressWrite;
|
|
+ private final ReferenceOpenHashSet<InProgressWrite> allPendingWrites = new ReferenceOpenHashSet<>();
|
|
+
|
|
+ private RegionDataController.ReadData readData;
|
|
+ private RegionDataController.WriteData writeData;
|
|
+ private boolean failedWrite;
|
|
+
|
|
+ public ChunkIOTask(final ServerLevel world, final RegionDataController regionDataController,
|
|
+ final int chunkX, final int chunkZ, final Priority priority, final InProgressRead inProgressRead) {
|
|
+ this.world = world;
|
|
+ this.regionDataController = regionDataController;
|
|
+ this.chunkX = chunkX;
|
|
+ this.chunkZ = chunkZ;
|
|
+ this.priority = priority;
|
|
+ this.inProgressRead = inProgressRead;
|
|
+ }
|
|
+
|
|
+ public Priority getPriority() {
|
|
+ synchronized (this) {
|
|
+ return this.priority;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // must hold lock on this object
|
|
+ private void updatePriority(final Priority priority) {
|
|
+ this.priority = priority;
|
|
+ if (this.currentTask != null) {
|
|
+ this.currentTask.setPriority(priority);
|
|
+ }
|
|
+ for (final InProgressWrite write : this.allPendingWrites) {
|
|
+ if (write.writeTask != null) {
|
|
+ write.writeTask.setPriority(priority);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean setPriority(final Priority priority) {
|
|
+ synchronized (this) {
|
|
+ if (this.priority == priority) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ this.updatePriority(priority);
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean raisePriority(final Priority priority) {
|
|
+ synchronized (this) {
|
|
+ if (this.priority.isHigherOrEqualPriority(priority)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ this.updatePriority(priority);
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean lowerPriority(final Priority priority) {
|
|
+ synchronized (this) {
|
|
+ if (this.priority.isLowerOrEqualPriority(priority)) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ this.updatePriority(priority);
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void pushPendingWrite(final InProgressWrite write) {
|
|
+ this.inProgressWrite = write;
|
|
+ synchronized (this) {
|
|
+ this.allPendingWrites.add(write);
|
|
+ if (write.writeTask != null) {
|
|
+ write.writeTask.setPriority(this.priority);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void pendingWriteComplete(final InProgressWrite write) {
|
|
+ synchronized (this) {
|
|
+ this.allPendingWrites.remove(write);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void scheduleReadIO() {
|
|
+ final PrioritisedExecutor.PrioritisedTask task;
|
|
+ synchronized (this) {
|
|
+ task = this.regionDataController.ioScheduler.createTask(this.chunkX, this.chunkZ, this::performReadIO, this.priority);
|
|
+ this.currentTask = task;
|
|
+ }
|
|
+ task.queue();
|
|
+ }
|
|
+
|
|
+ private void performReadIO() {
|
|
+ final InProgressRead read = this.inProgressRead;
|
|
+ final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
|
|
+
|
|
+ final boolean[] canRead = new boolean[] { true };
|
|
+
|
|
+ if (read.hasNoWaiters()) {
|
|
+ // cancelled read? go to task controller to confirm
|
|
+ final ChunkIOTask inMap = this.regionDataController.chunkTasks.compute(chunkKey, (final long keyInMap, final ChunkIOTask valueInMap) -> {
|
|
+ if (valueInMap == null) {
|
|
+ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkIOTask.this.toString() + ", report this!");
|
|
+ }
|
|
+ if (valueInMap != ChunkIOTask.this) {
|
|
+ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkIOTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
|
|
+ }
|
|
+
|
|
+ if (!read.hasNoWaiters()) {
|
|
+ return valueInMap;
|
|
+ } else {
|
|
+ canRead[0] = false;
|
|
+ }
|
|
+
|
|
+ if (valueInMap.inProgressWrite != null) {
|
|
+ return valueInMap;
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ });
|
|
+
|
|
+ if (inMap == null) {
|
|
+ this.regionDataController.endTask(this);
|
|
+ // read is cancelled - and no write pending, so we're done
|
|
+ return;
|
|
+ }
|
|
+ // if there is a write in progress, we don't actually have to worry about waiters gaining new entries -
|
|
+ // the readers will just use the in progress write, so the value in canRead is good to use without
|
|
+ // further synchronisation.
|
|
+ }
|
|
+
|
|
+ if (canRead[0]) {
|
|
+ RegionDataController.ReadData readData = null;
|
|
+ Throwable throwable = null;
|
|
+
|
|
+ try {
|
|
+ readData = this.regionDataController.readData(this.chunkX, this.chunkZ);
|
|
+ } catch (final Throwable thr) {
|
|
+ throwable = thr;
|
|
+ LOGGER.error("Failed to read chunk data for task: " + this.toString(), thr);
|
|
+ }
|
|
+
|
|
+ if (throwable != null) {
|
|
+ this.finishRead(null, throwable);
|
|
+ } else {
|
|
+ switch (readData.result()) {
|
|
+ case NO_DATA:
|
|
+ case SYNC_READ: {
|
|
+ this.finishRead(readData.syncRead(), null);
|
|
+ break;
|
|
+ }
|
|
+ case HAS_DATA: {
|
|
+ this.readData = readData;
|
|
+ this.scheduleReadDecompress();
|
|
+ // read will handle write scheduling
|
|
+ return;
|
|
+ }
|
|
+ default: {
|
|
+ throw new IllegalStateException("Unknown state: " + readData.result());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!this.tryAbortWrite()) {
|
|
+ this.scheduleWriteCompress();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void scheduleReadDecompress() {
|
|
+ final PrioritisedExecutor.PrioritisedTask task;
|
|
+ synchronized (this) {
|
|
+ task = this.regionDataController.compressionExecutor.createTask(this::performReadDecompress, this.priority);
|
|
+ this.currentTask = task;
|
|
+ }
|
|
+ task.queue();
|
|
+ }
|
|
+
|
|
+ private void performReadDecompress() {
|
|
+ final RegionDataController.ReadData readData = this.readData;
|
|
+ this.readData = null;
|
|
+
|
|
+ CompoundTag compoundTag = null;
|
|
+ Throwable throwable = null;
|
|
+
|
|
+ try {
|
|
+ compoundTag = this.regionDataController.finishRead(this.chunkX, this.chunkZ, readData);
|
|
+ } catch (final Throwable thr) {
|
|
+ throwable = thr;
|
|
+ LOGGER.error("Failed to decompress chunk data for task: " + this.toString(), thr);
|
|
+ }
|
|
+
|
|
+ if (compoundTag == null) {
|
|
+ // need to re-try from the start
|
|
+ this.scheduleReadIO();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ this.finishRead(compoundTag, throwable);
|
|
+ if (!this.tryAbortWrite()) {
|
|
+ this.scheduleWriteCompress();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void finishRead(final CompoundTag compoundTag, final Throwable throwable) {
|
|
+ this.inProgressRead.complete(this, compoundTag, throwable);
|
|
+ }
|
|
+
|
|
+ public void scheduleWriteCompress() {
|
|
+ final InProgressWrite inProgressWrite = this.inProgressWrite;
|
|
+
|
|
+ final PrioritisedExecutor.PrioritisedTask task;
|
|
+ synchronized (this) {
|
|
+ task = this.regionDataController.compressionExecutor.createTask(() -> {
|
|
+ ChunkIOTask.this.performWriteCompress(inProgressWrite);
|
|
+ }, this.priority);
|
|
+ this.currentTask = task;
|
|
+ }
|
|
+
|
|
+ inProgressWrite.addToWaiters(this, (final CompoundTag data, final Throwable throwable) -> {
|
|
+ task.queue();
|
|
+ });
|
|
+ }
|
|
+
|
|
+ private boolean tryAbortWrite() {
|
|
+ final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
|
|
+ if (this.inProgressWrite == null) {
|
|
+ final ChunkIOTask inMap = this.regionDataController.chunkTasks.compute(chunkKey, (final long keyInMap, final ChunkIOTask valueInMap) -> {
|
|
+ if (valueInMap == null) {
|
|
+ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkIOTask.this.toString() + ", report this!");
|
|
+ }
|
|
+ if (valueInMap != ChunkIOTask.this) {
|
|
+ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkIOTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
|
|
+ }
|
|
+
|
|
+ if (valueInMap.inProgressWrite != null) {
|
|
+ return valueInMap;
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ });
|
|
+
|
|
+ if (inMap == null) {
|
|
+ this.regionDataController.endTask(this);
|
|
+ return true; // set the task value to null, indicating we're done
|
|
+ } // else: inProgressWrite changed, so now we have something to write
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ private void performWriteCompress(final InProgressWrite inProgressWrite) {
|
|
+ final CompoundTag write = inProgressWrite.value;
|
|
+ if (!inProgressWrite.isComplete()) {
|
|
+ throw new IllegalStateException("Should be writable");
|
|
+ }
|
|
+
|
|
+ RegionDataController.WriteData writeData = null;
|
|
+ boolean failedWrite = false;
|
|
+
|
|
+ try {
|
|
+ writeData = this.regionDataController.startWrite(this.chunkX, this.chunkZ, write);
|
|
+ } catch (final Throwable thr) {
|
|
+ // TODO implement this?
|
|
+ /*if (thr instanceof RegionFileStorage.RegionFileSizeException) {
|
|
+ final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024);
|
|
+ LOGGER.error("Chunk at (" + this.chunkX + "," + this.chunkZ + ") in '" + WorldUtil.getWorldName(this.world) + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk.");
|
|
+ } else */
|
|
+ {
|
|
+ failedWrite = thr instanceof IOException;
|
|
+ LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (writeData == null) {
|
|
+ // null if a throwable was encountered
|
|
+
|
|
+ // we cannot continue to the I/O stage here, so try to complete
|
|
+
|
|
+ if (this.tryCompleteWrite(inProgressWrite, failedWrite)) {
|
|
+ return;
|
|
+ } else {
|
|
+ // fetch new data and try again
|
|
+ this.scheduleWriteCompress();
|
|
+ return;
|
|
+ }
|
|
+ } else {
|
|
+ // writeData != null && !failedWrite
|
|
+ // we can continue to I/O stage
|
|
+ this.writeData = writeData;
|
|
+ this.scheduleWriteIO(inProgressWrite);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void scheduleWriteIO(final InProgressWrite inProgressWrite) {
|
|
+ final PrioritisedExecutor.PrioritisedTask task;
|
|
+ synchronized (this) {
|
|
+ task = this.regionDataController.ioScheduler.createTask(this.chunkX, this.chunkZ, () -> {
|
|
+ ChunkIOTask.this.runWriteIO(inProgressWrite);
|
|
+ }, this.priority);
|
|
+ this.currentTask = task;
|
|
+ }
|
|
+ task.queue();
|
|
+ }
|
|
+
|
|
+ private void runWriteIO(final InProgressWrite inProgressWrite) {
|
|
+ RegionDataController.WriteData writeData = this.writeData;
|
|
+ this.writeData = null;
|
|
+
|
|
+ boolean failedWrite = false;
|
|
+
|
|
+ try {
|
|
+ this.regionDataController.finishWrite(this.chunkX, this.chunkZ, writeData);
|
|
+ } catch (final Throwable thr) {
|
|
+ failedWrite = thr instanceof IOException;
|
|
+ LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
|
|
+ }
|
|
+
|
|
+ if (!this.tryCompleteWrite(inProgressWrite, failedWrite)) {
|
|
+ // fetch new data and try again
|
|
+ this.scheduleWriteCompress();
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ private boolean tryCompleteWrite(final InProgressWrite written, final boolean failedWrite) {
|
|
+ final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
|
|
+
|
|
+ final boolean[] done = new boolean[] { false };
|
|
+
|
|
+ this.regionDataController.chunkTasks.compute(chunkKey, (final long keyInMap, final ChunkIOTask valueInMap) -> {
|
|
+ if (valueInMap == null) {
|
|
+ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkIOTask.this.toString() + ", report this!");
|
|
+ }
|
|
+ if (valueInMap != ChunkIOTask.this) {
|
|
+ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkIOTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
|
|
+ }
|
|
+ if (valueInMap.inProgressWrite == written) {
|
|
+ valueInMap.failedWrite = failedWrite;
|
|
+ done[0] = true;
|
|
+ // keep the data in map if we failed the write so we can try to prevent data loss
|
|
+ return failedWrite ? valueInMap : null;
|
|
+ }
|
|
+ // different data than expected, means we need to retry write
|
|
+ return valueInMap;
|
|
+ });
|
|
+
|
|
+ if (done[0]) {
|
|
+ this.regionDataController.endTask(this);
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public String toString() {
|
|
+ return "Task for world: '" + WorldUtil.getWorldName(this.world) + "' at (" + this.chunkX + ","
|
|
+ + this.chunkZ + ") type: " + this.regionDataController.type.name() + ", hash: " + this.hashCode();
|
|
+ }
|
|
+
|
|
+ private static final class InProgressRead {
|
|
+
|
|
+ private static final Logger LOGGER = LoggerFactory.getLogger(InProgressRead.class);
|
|
+
|
|
+ private CompoundTag value;
|
|
+ private Throwable throwable;
|
|
+ private final MultiThreadedQueue<BiConsumer<CompoundTag, Throwable>> callbacks = new MultiThreadedQueue<>();
|
|
+
|
|
+ public boolean hasNoWaiters() {
|
|
+ return this.callbacks.isEmpty();
|
|
+ }
|
|
+
|
|
+ public boolean addToAsyncWaiters(final BiConsumer<CompoundTag, Throwable> callback) {
|
|
+ return this.callbacks.add(callback);
|
|
+ }
|
|
+
|
|
+ public boolean cancel(final BiConsumer<CompoundTag, Throwable> callback) {
|
|
+ return this.callbacks.remove(callback);
|
|
+ }
|
|
+
|
|
+ public void complete(final ChunkIOTask task, final CompoundTag value, final Throwable throwable) {
|
|
+ this.value = value;
|
|
+ this.throwable = throwable;
|
|
+
|
|
+ BiConsumer<CompoundTag, Throwable> consumer;
|
|
+ while ((consumer = this.callbacks.pollOrBlockAdds()) != null) {
|
|
+ try {
|
|
+ consumer.accept(value == null ? null : value.copy(), throwable);
|
|
+ } catch (final Throwable thr) {
|
|
+ LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data (read) for task " + task.toString(), thr);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final class InProgressWrite {
|
|
+
|
|
+ private static final Logger LOGGER = LoggerFactory.getLogger(InProgressWrite.class);
|
|
+
|
|
+ private CompoundTag value;
|
|
+ private Throwable throwable;
|
|
+ private volatile boolean complete;
|
|
+ private final MultiThreadedQueue<BiConsumer<CompoundTag, Throwable>> callbacks = new MultiThreadedQueue<>();
|
|
+
|
|
+ private final PrioritisedExecutor.PrioritisedTask writeTask;
|
|
+
|
|
+ public InProgressWrite(final PrioritisedExecutor.PrioritisedTask writeTask) {
|
|
+ this.writeTask = writeTask;
|
|
+ }
|
|
+
|
|
+ public boolean isComplete() {
|
|
+ return this.complete;
|
|
+ }
|
|
+
|
|
+ public void schedule(final ChunkIOTask task, final Consumer<BiConsumer<CompoundTag, Throwable>> scheduler) {
|
|
+ scheduler.accept((final CompoundTag data, final Throwable throwable) -> {
|
|
+ InProgressWrite.this.complete(task, data, throwable);
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public boolean addToAsyncWaiters(final BiConsumer<CompoundTag, Throwable> callback) {
|
|
+ return this.callbacks.add(callback);
|
|
+ }
|
|
+
|
|
+ public void addToWaiters(final ChunkIOTask task, final BiConsumer<CompoundTag, Throwable> consumer) {
|
|
+ if (!this.callbacks.add(consumer)) {
|
|
+ this.syncAccept(task, consumer, this.value, this.throwable);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void syncAccept(final ChunkIOTask task, final BiConsumer<CompoundTag, Throwable> consumer, final CompoundTag value, final Throwable throwable) {
|
|
+ try {
|
|
+ consumer.accept(value == null ? null : value.copy(), throwable);
|
|
+ } catch (final Throwable thr) {
|
|
+ LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data (write) for task " + task.toString(), thr);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void complete(final ChunkIOTask task, final CompoundTag value, final Throwable throwable) {
|
|
+ this.value = value;
|
|
+ this.throwable = throwable;
|
|
+ this.complete = true;
|
|
+
|
|
+ task.pendingWriteComplete(this);
|
|
+
|
|
+ BiConsumer<CompoundTag, Throwable> consumer;
|
|
+ while ((consumer = this.callbacks.pollOrBlockAdds()) != null) {
|
|
+ this.syncAccept(task, consumer, value, throwable);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public boolean cancel(final BiConsumer<CompoundTag, Throwable> callback) {
|
|
+ return this.callbacks.remove(callback);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static abstract class RegionDataController {
|
|
+
|
|
+ public final RegionFileType type;
|
|
+ private final PrioritisedExecutor compressionExecutor;
|
|
+ private final IOScheduler ioScheduler;
|
|
+ private final ConcurrentLong2ReferenceChainedHashTable<ChunkIOTask> chunkTasks = new ConcurrentLong2ReferenceChainedHashTable<>();
|
|
+
|
|
+ private final AtomicLong inProgressTasks = new AtomicLong();
|
|
+
|
|
+ public RegionDataController(final RegionFileType type, final PrioritisedExecutor ioExecutor,
|
|
+ final PrioritisedExecutor compressionExecutor) {
|
|
+ this.type = type;
|
|
+ this.compressionExecutor = compressionExecutor;
|
|
+ this.ioScheduler = new IOScheduler(ioExecutor);
|
|
+ }
|
|
+
|
|
+ final void startTask(final ChunkIOTask task) {
|
|
+ this.inProgressTasks.getAndIncrement();
|
|
+ }
|
|
+
|
|
+ final void endTask(final ChunkIOTask task) {
|
|
+ this.inProgressTasks.getAndDecrement();
|
|
+ }
|
|
+
|
|
+ public boolean hasTasks() {
|
|
+ return this.inProgressTasks.get() != 0L;
|
|
+ }
|
|
+
|
|
+ public long getTotalWorkingTasks() {
|
|
+ return this.inProgressTasks.get();
|
|
+ }
|
|
+
|
|
+ public abstract RegionFileStorage getCache();
|
|
+
|
|
+ public static record WriteData(CompoundTag input, WriteResult result, DataOutputStream output, IORunnable write) {
|
|
+ public static enum WriteResult {
|
|
+ WRITE,
|
|
+ DELETE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public abstract WriteData startWrite(final int chunkX, final int chunkZ, final CompoundTag compound) throws IOException;
|
|
+
|
|
+ public abstract void finishWrite(final int chunkX, final int chunkZ, final WriteData writeData) throws IOException;
|
|
+
|
|
+ public static record ReadData(ReadResult result, DataInputStream input, CompoundTag syncRead) {
|
|
+ public static enum ReadResult {
|
|
+ NO_DATA,
|
|
+ HAS_DATA,
|
|
+ SYNC_READ;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public abstract ReadData readData(final int chunkX, final int chunkZ) throws IOException;
|
|
+
|
|
+ // if the return value is null, then the caller needs to re-try with a new call to readData()
|
|
+ public abstract CompoundTag finishRead(final int chunkX, final int chunkZ, final ReadData readData) throws IOException;
|
|
+
|
|
+ public static interface IORunnable {
|
|
+
|
|
+ public void run(final RegionFile regionFile) throws IOException;
|
|
+
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final class IOScheduler {
|
|
+
|
|
+ private final ConcurrentLong2ReferenceChainedHashTable<RegionIOTasks> regionTasks = new ConcurrentLong2ReferenceChainedHashTable<>();
|
|
+ private final PrioritisedExecutor executor;
|
|
+
|
|
+ public IOScheduler(final PrioritisedExecutor executor) {
|
|
+ this.executor = executor;
|
|
+ }
|
|
+
|
|
+ public PrioritisedExecutor.PrioritisedTask createTask(final int chunkX, final int chunkZ,
|
|
+ final Runnable run, final Priority priority) {
|
|
+ final PrioritisedExecutor.PrioritisedTask[] ret = new PrioritisedExecutor.PrioritisedTask[1];
|
|
+ final long subOrder = this.executor.generateNextSubOrder();
|
|
+ this.regionTasks.compute(CoordinateUtils.getChunkKey(chunkX >> REGION_FILE_SHIFT, chunkZ >> REGION_FILE_SHIFT),
|
|
+ (final long regionKey, final RegionIOTasks existing) -> {
|
|
+ final RegionIOTasks res;
|
|
+ if (existing != null) {
|
|
+ res = existing;
|
|
+ } else {
|
|
+ res = new RegionIOTasks(regionKey, IOScheduler.this);
|
|
+ }
|
|
+
|
|
+ ret[0] = res.createTask(run, priority, subOrder);
|
|
+
|
|
+ return res;
|
|
+ });
|
|
+
|
|
+ return ret[0];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final class RegionIOTasks implements Runnable {
|
|
+
|
|
+ private static final Logger LOGGER = LoggerFactory.getLogger(RegionIOTasks.class);
|
|
+
|
|
+ private final PrioritisedTaskQueue queue = new PrioritisedTaskQueue();
|
|
+ private final long regionKey;
|
|
+ private final IOScheduler ioScheduler;
|
|
+ private long createdTasks;
|
|
+ private long executedTasks;
|
|
+
|
|
+ private PrioritisedExecutor.PrioritisedTask task;
|
|
+
|
|
+ public RegionIOTasks(final long regionKey, final IOScheduler ioScheduler) {
|
|
+ this.regionKey = regionKey;
|
|
+ this.ioScheduler = ioScheduler;
|
|
+ }
|
|
+
|
|
+ public PrioritisedExecutor.PrioritisedTask createTask(final Runnable run, final Priority priority,
|
|
+ final long subOrder) {
|
|
+ ++this.createdTasks;
|
|
+ return new WrappedTask(this.queue.createTask(run, priority, subOrder));
|
|
+ }
|
|
+
|
|
+ private void adjustTaskPriority() {
|
|
+ final PrioritisedTaskQueue.PrioritySubOrderPair priority = this.queue.getHighestPrioritySubOrder();
|
|
+ if (this.task == null) {
|
|
+ if (priority == null) {
|
|
+ return;
|
|
+ }
|
|
+ this.task = this.ioScheduler.executor.createTask(this, priority.priority(), priority.subOrder());
|
|
+ this.task.queue();
|
|
+ } else {
|
|
+ if (priority == null) {
|
|
+ throw new IllegalStateException();
|
|
+ } else {
|
|
+ this.task.setPriorityAndSubOrder(priority.priority(), priority.subOrder());
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void run() {
|
|
+ final Runnable run;
|
|
+ synchronized (this) {
|
|
+ run = this.queue.pollTask();
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ run.run();
|
|
+ } finally {
|
|
+ synchronized (this) {
|
|
+ this.task = null;
|
|
+ this.adjustTaskPriority();
|
|
+ }
|
|
+ this.ioScheduler.regionTasks.compute(this.regionKey, (final long keyInMap, final RegionIOTasks tasks) -> {
|
|
+ if (tasks != RegionIOTasks.this) {
|
|
+ throw new IllegalStateException("Region task mismatch");
|
|
+ }
|
|
+ ++tasks.executedTasks;
|
|
+ if (tasks.createdTasks != tasks.executedTasks) {
|
|
+ return tasks;
|
|
+ }
|
|
+
|
|
+ if (tasks.task != null) {
|
|
+ throw new IllegalStateException("Task may not be null when created==executed");
|
|
+ }
|
|
+
|
|
+ return null;
|
|
+ });
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private final class WrappedTask implements PrioritisedExecutor.PrioritisedTask {
|
|
+
|
|
+ private final PrioritisedExecutor.PrioritisedTask wrapped;
|
|
+
|
|
+ public WrappedTask(final PrioritisedExecutor.PrioritisedTask wrap) {
|
|
+ this.wrapped = wrap;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public PrioritisedExecutor getExecutor() {
|
|
+ return RegionIOTasks.this.ioScheduler.executor;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean queue() {
|
|
+ synchronized (RegionIOTasks.this) {
|
|
+ if (this.wrapped.queue()) {
|
|
+ RegionIOTasks.this.adjustTaskPriority();
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isQueued() {
|
|
+ return this.wrapped.isQueued();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean cancel() {
|
|
+ throw new UnsupportedOperationException();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean execute() {
|
|
+ throw new UnsupportedOperationException();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Priority getPriority() {
|
|
+ return this.wrapped.getPriority();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean setPriority(final Priority priority) {
|
|
+ synchronized (RegionIOTasks.this) {
|
|
+ if (this.wrapped.setPriority(priority) && this.wrapped.isQueued()) {
|
|
+ RegionIOTasks.this.adjustTaskPriority();
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean raisePriority(final Priority priority) {
|
|
+ synchronized (RegionIOTasks.this) {
|
|
+ if (this.wrapped.raisePriority(priority) && this.wrapped.isQueued()) {
|
|
+ RegionIOTasks.this.adjustTaskPriority();
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean lowerPriority(final Priority priority) {
|
|
+ synchronized (RegionIOTasks.this) {
|
|
+ if (this.wrapped.lowerPriority(priority) && this.wrapped.isQueued()) {
|
|
+ RegionIOTasks.this.adjustTaskPriority();
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public long getSubOrder() {
|
|
+ return this.wrapped.getSubOrder();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean setSubOrder(final long subOrder) {
|
|
+ synchronized (RegionIOTasks.this) {
|
|
+ if (this.wrapped.setSubOrder(subOrder) && this.wrapped.isQueued()) {
|
|
+ RegionIOTasks.this.adjustTaskPriority();
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean raiseSubOrder(final long subOrder) {
|
|
+ synchronized (RegionIOTasks.this) {
|
|
+ if (this.wrapped.raiseSubOrder(subOrder) && this.wrapped.isQueued()) {
|
|
+ RegionIOTasks.this.adjustTaskPriority();
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean lowerSubOrder(final long subOrder) {
|
|
+ synchronized (RegionIOTasks.this) {
|
|
+ if (this.wrapped.lowerSubOrder(subOrder) && this.wrapped.isQueued()) {
|
|
+ RegionIOTasks.this.adjustTaskPriority();
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean setPriorityAndSubOrder(final Priority priority, final long subOrder) {
|
|
+ synchronized (RegionIOTasks.this) {
|
|
+ if (this.wrapped.setPriorityAndSubOrder(priority, subOrder) && this.wrapped.isQueued()) {
|
|
+ RegionIOTasks.this.adjustTaskPriority();
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java
|
|
deleted file mode 100644
|
|
index 3218cbf84f54daf06e84442d5eb1a36d8da6b215..0000000000000000000000000000000000000000
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java
|
|
+++ /dev/null
|
|
@@ -1,1240 +0,0 @@
|
|
-package ca.spottedleaf.moonrise.patches.chunk_system.io;
|
|
-
|
|
-import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
|
-import ca.spottedleaf.concurrentutil.executor.Cancellable;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedQueueExecutorThread;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadedTaskQueue;
|
|
-import ca.spottedleaf.concurrentutil.function.BiLong1Function;
|
|
-import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
|
-import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
-import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
-import ca.spottedleaf.moonrise.common.util.TickThread;
|
|
-import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
|
-import net.minecraft.nbt.CompoundTag;
|
|
-import net.minecraft.server.level.ServerLevel;
|
|
-import net.minecraft.world.level.ChunkPos;
|
|
-import net.minecraft.world.level.chunk.storage.RegionFile;
|
|
-import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
|
-import org.slf4j.Logger;
|
|
-import org.slf4j.LoggerFactory;
|
|
-import java.io.IOException;
|
|
-import java.lang.invoke.VarHandle;
|
|
-import java.util.concurrent.CompletableFuture;
|
|
-import java.util.concurrent.CompletionException;
|
|
-import java.util.concurrent.atomic.AtomicInteger;
|
|
-import java.util.function.BiConsumer;
|
|
-import java.util.function.Consumer;
|
|
-import java.util.function.Function;
|
|
-
|
|
-/**
|
|
- * Prioritised RegionFile I/O executor, responsible for all RegionFile access.
|
|
- * <p>
|
|
- * All functions provided are MT-Safe, however certain ordering constraints are recommended:
|
|
- * <li>
|
|
- * Chunk saves may not occur for unloaded chunks.
|
|
- * </li>
|
|
- * <li>
|
|
- * Tasks must be scheduled on the chunk scheduler thread.
|
|
- * </li>
|
|
- * By following these constraints, no chunk data loss should occur with the exception of underlying I/O problems.
|
|
- * </p>
|
|
- */
|
|
-public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
|
|
-
|
|
- private static final Logger LOGGER = LoggerFactory.getLogger(RegionFileIOThread.class);
|
|
-
|
|
- /**
|
|
- * The kinds of region files controlled by the region file thread. Add more when needed, and ensure
|
|
- * getControllerFor is updated.
|
|
- */
|
|
- public static enum RegionFileType {
|
|
- CHUNK_DATA,
|
|
- POI_DATA,
|
|
- ENTITY_DATA;
|
|
- }
|
|
-
|
|
- private static final RegionFileType[] CACHED_REGIONFILE_TYPES = RegionFileType.values();
|
|
-
|
|
- public static ChunkDataController getControllerFor(final ServerLevel world, final RegionFileType type) {
|
|
- switch (type) {
|
|
- case CHUNK_DATA:
|
|
- return ((ChunkSystemServerLevel)world).moonrise$getChunkDataController();
|
|
- case POI_DATA:
|
|
- return ((ChunkSystemServerLevel)world).moonrise$getPoiChunkDataController();
|
|
- case ENTITY_DATA:
|
|
- return ((ChunkSystemServerLevel)world).moonrise$getEntityChunkDataController();
|
|
- default:
|
|
- throw new IllegalStateException("Unknown controller type " + type);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Collects regionfile data for a certain chunk.
|
|
- */
|
|
- public static final class RegionFileData {
|
|
-
|
|
- private final boolean[] hasResult = new boolean[CACHED_REGIONFILE_TYPES.length];
|
|
- private final CompoundTag[] data = new CompoundTag[CACHED_REGIONFILE_TYPES.length];
|
|
- private final Throwable[] throwables = new Throwable[CACHED_REGIONFILE_TYPES.length];
|
|
-
|
|
- /**
|
|
- * Sets the result associated with the specified regionfile type. Note that
|
|
- * results can only be set once per regionfile type.
|
|
- *
|
|
- * @param type The regionfile type.
|
|
- * @param data The result to set.
|
|
- */
|
|
- public void setData(final RegionFileType type, final CompoundTag data) {
|
|
- final int index = type.ordinal();
|
|
-
|
|
- if (this.hasResult[index]) {
|
|
- throw new IllegalArgumentException("Result already exists for type " + type);
|
|
- }
|
|
- this.hasResult[index] = true;
|
|
- this.data[index] = data;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Sets the result associated with the specified regionfile type. Note that
|
|
- * results can only be set once per regionfile type.
|
|
- *
|
|
- * @param type The regionfile type.
|
|
- * @param throwable The result to set.
|
|
- */
|
|
- public void setThrowable(final RegionFileType type, final Throwable throwable) {
|
|
- final int index = type.ordinal();
|
|
-
|
|
- if (this.hasResult[index]) {
|
|
- throw new IllegalArgumentException("Result already exists for type " + type);
|
|
- }
|
|
- this.hasResult[index] = true;
|
|
- this.throwables[index] = throwable;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns whether there is a result for the specified regionfile type.
|
|
- *
|
|
- * @param type Specified regionfile type.
|
|
- *
|
|
- * @return Whether a result exists for {@code type}.
|
|
- */
|
|
- public boolean hasResult(final RegionFileType type) {
|
|
- return this.hasResult[type.ordinal()];
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the data result for the regionfile type.
|
|
- *
|
|
- * @param type Specified regionfile type.
|
|
- *
|
|
- * @throws IllegalArgumentException If the result has not been set for {@code type}.
|
|
- * @return The data result for the specified type. If the result is a {@code Throwable},
|
|
- * then returns {@code null}.
|
|
- */
|
|
- public CompoundTag getData(final RegionFileType type) {
|
|
- final int index = type.ordinal();
|
|
-
|
|
- if (!this.hasResult[index]) {
|
|
- throw new IllegalArgumentException("Result does not exist for type " + type);
|
|
- }
|
|
-
|
|
- return this.data[index];
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the throwable result for the regionfile type.
|
|
- *
|
|
- * @param type Specified regionfile type.
|
|
- *
|
|
- * @throws IllegalArgumentException If the result has not been set for {@code type}.
|
|
- * @return The throwable result for the specified type. If the result is an {@code CompoundTag},
|
|
- * then returns {@code null}.
|
|
- */
|
|
- public Throwable getThrowable(final RegionFileType type) {
|
|
- final int index = type.ordinal();
|
|
-
|
|
- if (!this.hasResult[index]) {
|
|
- throw new IllegalArgumentException("Result does not exist for type " + type);
|
|
- }
|
|
-
|
|
- return this.throwables[index];
|
|
- }
|
|
- }
|
|
-
|
|
- private static final Object INIT_LOCK = new Object();
|
|
-
|
|
- static RegionFileIOThread[] threads;
|
|
-
|
|
- /* needs to be consistent given a set of parameters */
|
|
- static RegionFileIOThread selectThread(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
|
|
- if (threads == null) {
|
|
- throw new IllegalStateException("Threads not initialised");
|
|
- }
|
|
-
|
|
- final int regionX = chunkX >> 5;
|
|
- final int regionZ = chunkZ >> 5;
|
|
- final int typeOffset = type.ordinal();
|
|
-
|
|
- return threads[(System.identityHashCode(world) + regionX + regionZ + typeOffset) % threads.length];
|
|
- }
|
|
-
|
|
- /**
|
|
- * Shuts down the I/O executor(s). Watis for all tasks to complete if specified.
|
|
- * Tasks queued during this call might not be accepted, and tasks queued after will not be accepted.
|
|
- *
|
|
- * @param wait Whether to wait until all tasks have completed.
|
|
- */
|
|
- public static void close(final boolean wait) {
|
|
- for (int i = 0, len = threads.length; i < len; ++i) {
|
|
- threads[i].close(false, true);
|
|
- }
|
|
- if (wait) {
|
|
- RegionFileIOThread.flush();
|
|
- }
|
|
- }
|
|
-
|
|
- public static long[] getExecutedTasks() {
|
|
- final long[] ret = new long[threads.length];
|
|
- for (int i = 0, len = threads.length; i < len; ++i) {
|
|
- ret[i] = threads[i].getTotalTasksExecuted();
|
|
- }
|
|
-
|
|
- return ret;
|
|
- }
|
|
-
|
|
- public static long[] getTasksScheduled() {
|
|
- final long[] ret = new long[threads.length];
|
|
- for (int i = 0, len = threads.length; i < len; ++i) {
|
|
- ret[i] = threads[i].getTotalTasksScheduled();
|
|
- }
|
|
- return ret;
|
|
- }
|
|
-
|
|
- public static void flush() {
|
|
- for (int i = 0, len = threads.length; i < len; ++i) {
|
|
- threads[i].waitUntilAllExecuted();
|
|
- }
|
|
- }
|
|
-
|
|
- public static void flushRegionStorages(final ServerLevel world) throws IOException {
|
|
- for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
|
|
- getControllerFor(world, type).getCache().flush();
|
|
- }
|
|
- }
|
|
-
|
|
- public static void partialFlush(final int totalTasksRemaining) {
|
|
- long failures = 1L; // start out at 0.25ms
|
|
-
|
|
- for (;;) {
|
|
- final long[] executed = getExecutedTasks();
|
|
- final long[] scheduled = getTasksScheduled();
|
|
-
|
|
- long sum = 0;
|
|
- for (int i = 0; i < executed.length; ++i) {
|
|
- sum += scheduled[i] - executed[i];
|
|
- }
|
|
-
|
|
- if (sum <= totalTasksRemaining) {
|
|
- break;
|
|
- }
|
|
-
|
|
- failures = ConcurrentUtil.linearLongBackoff(failures, 250_000L, 5_000_000L); // 500us, 5ms
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Inits the executor with the specified number of threads.
|
|
- *
|
|
- * @param threads Specified number of threads.
|
|
- */
|
|
- public static void init(final int threads) {
|
|
- synchronized (INIT_LOCK) {
|
|
- if (RegionFileIOThread.threads != null) {
|
|
- throw new IllegalStateException("Already initialised threads");
|
|
- }
|
|
-
|
|
- RegionFileIOThread.threads = new RegionFileIOThread[threads];
|
|
-
|
|
- for (int i = 0; i < threads; ++i) {
|
|
- RegionFileIOThread.threads[i] = new RegionFileIOThread(i);
|
|
- RegionFileIOThread.threads[i].start();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- public static void deinit() {
|
|
- if (true) { // Paper
|
|
- // TODO does this cause issues with mods? how to implement
|
|
- close(true);
|
|
- synchronized (INIT_LOCK) {
|
|
- RegionFileIOThread.threads = null;
|
|
- }
|
|
- } else { RegionFileIOThread.flush(); }
|
|
- }
|
|
-
|
|
- private RegionFileIOThread(final int threadNumber) {
|
|
- super(new PrioritisedThreadedTaskQueue(), (int)(1.0e6)); // 1.0ms spinwait time
|
|
- this.setName("RegionFile I/O Thread #" + threadNumber);
|
|
- this.setPriority(Thread.NORM_PRIORITY - 2); // we keep priority close to normal because threads can wait on us
|
|
- this.setUncaughtExceptionHandler((final Thread thread, final Throwable thr) -> {
|
|
- LOGGER.error("Uncaught exception thrown from I/O thread, report this! Thread: " + thread.getName(), thr);
|
|
- });
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns whether the current thread is a regionfile I/O executor.
|
|
- * @return Whether the current thread is a regionfile I/O executor.
|
|
- */
|
|
- public static boolean isRegionFileThread() {
|
|
- return Thread.currentThread() instanceof RegionFileIOThread;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the priority associated with blocking I/O based on the current thread. The goal is to avoid
|
|
- * dumb plugins from taking away priority from threads we consider crucial.
|
|
- * @return The priroity to use with blocking I/O on the current thread.
|
|
- */
|
|
- public static Priority getIOBlockingPriorityForCurrentThread() {
|
|
- if (TickThread.isTickThread()) {
|
|
- return Priority.BLOCKING;
|
|
- }
|
|
- return Priority.HIGHEST;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the current {@code CompoundTag} pending for write for the specified chunk & regionfile type.
|
|
- * Note that this does not copy the result, so do not modify the result returned.
|
|
- *
|
|
- * @param world Specified world.
|
|
- * @param chunkX Specified chunk x.
|
|
- * @param chunkZ Specified chunk z.
|
|
- * @param type Specified regionfile type.
|
|
- *
|
|
- * @return The compound tag associated for the specified chunk. {@code null} if no write was pending, or if {@code null} is the write pending.
|
|
- */
|
|
- public static CompoundTag getPendingWrite(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
|
|
- final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
|
|
- return thread.getPendingWriteInternal(world, chunkX, chunkZ, type);
|
|
- }
|
|
-
|
|
- CompoundTag getPendingWriteInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
|
|
- final ChunkDataController taskController = getControllerFor(world, type);
|
|
- final ChunkDataTask task = taskController.tasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
-
|
|
- if (task == null) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- final CompoundTag ret = task.inProgressWrite;
|
|
-
|
|
- return ret == ChunkDataTask.NOTHING_TO_WRITE ? null : ret;
|
|
- }
|
|
-
|
|
- /**
|
|
- * Returns the priority for the specified regionfile type for the specified chunk.
|
|
- * @param world Specified world.
|
|
- * @param chunkX Specified chunk x.
|
|
- * @param chunkZ Specified chunk z.
|
|
- * @param type Specified regionfile type.
|
|
- * @return The priority for the chunk
|
|
- */
|
|
- public static Priority getPriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
|
|
- final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
|
|
- return thread.getPriorityInternal(world, chunkX, chunkZ, type);
|
|
- }
|
|
-
|
|
- Priority getPriorityInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
|
|
- final ChunkDataController taskController = getControllerFor(world, type);
|
|
- final ChunkDataTask task = taskController.tasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
-
|
|
- if (task == null) {
|
|
- return Priority.COMPLETING;
|
|
- }
|
|
-
|
|
- return task.prioritisedTask.getPriority();
|
|
- }
|
|
-
|
|
- /**
|
|
- * Sets the priority for all regionfile types for the specified chunk. Note that great care should
|
|
- * be taken using this method, as there can be multiple tasks tied to the same chunk that want different
|
|
- * priorities.
|
|
- *
|
|
- * @param world Specified world.
|
|
- * @param chunkX Specified chunk x.
|
|
- * @param chunkZ Specified chunk z.
|
|
- * @param priority New priority.
|
|
- *
|
|
- * @see #raisePriority(ServerLevel, int, int, Priority)
|
|
- * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- * @see #lowerPriority(ServerLevel, int, int, Priority)
|
|
- * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- */
|
|
- public static void setPriority(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final Priority priority) {
|
|
- for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
|
|
- RegionFileIOThread.setPriority(world, chunkX, chunkZ, type, priority);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Sets the priority for the specified regionfile type for the specified chunk. Note that great care should
|
|
- * be taken using this method, as there can be multiple tasks tied to the same chunk that want different
|
|
- * priorities.
|
|
- *
|
|
- * @param world Specified world.
|
|
- * @param chunkX Specified chunk x.
|
|
- * @param chunkZ Specified chunk z.
|
|
- * @param type Specified regionfile type.
|
|
- * @param priority New priority.
|
|
- *
|
|
- * @see #raisePriority(ServerLevel, int, int, Priority)
|
|
- * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- * @see #lowerPriority(ServerLevel, int, int, Priority)
|
|
- * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- */
|
|
- public static void setPriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
- final Priority priority) {
|
|
- final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
|
|
- thread.setPriorityInternal(world, chunkX, chunkZ, type, priority);
|
|
- }
|
|
-
|
|
- void setPriorityInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
- final Priority priority) {
|
|
- final ChunkDataController taskController = getControllerFor(world, type);
|
|
- final ChunkDataTask task = taskController.tasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
-
|
|
- if (task != null) {
|
|
- task.prioritisedTask.setPriority(priority);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Raises the priority for all regionfile types for the specified chunk.
|
|
- *
|
|
- * @param world Specified world.
|
|
- * @param chunkX Specified chunk x.
|
|
- * @param chunkZ Specified chunk z.
|
|
- * @param priority New priority.
|
|
- *
|
|
- * @see #setPriority(ServerLevel, int, int, Priority)
|
|
- * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- * @see #lowerPriority(ServerLevel, int, int, Priority)
|
|
- * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- */
|
|
- public static void raisePriority(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final Priority priority) {
|
|
- for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
|
|
- RegionFileIOThread.raisePriority(world, chunkX, chunkZ, type, priority);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Raises the priority for the specified regionfile type for the specified chunk.
|
|
- *
|
|
- * @param world Specified world.
|
|
- * @param chunkX Specified chunk x.
|
|
- * @param chunkZ Specified chunk z.
|
|
- * @param type Specified regionfile type.
|
|
- * @param priority New priority.
|
|
- *
|
|
- * @see #setPriority(ServerLevel, int, int, Priority)
|
|
- * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- * @see #lowerPriority(ServerLevel, int, int, Priority)
|
|
- * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- */
|
|
- public static void raisePriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
- final Priority priority) {
|
|
- final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
|
|
- thread.raisePriorityInternal(world, chunkX, chunkZ, type, priority);
|
|
- }
|
|
-
|
|
- void raisePriorityInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
- final Priority priority) {
|
|
- final ChunkDataController taskController = getControllerFor(world, type);
|
|
- final ChunkDataTask task = taskController.tasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
-
|
|
- if (task != null) {
|
|
- task.prioritisedTask.raisePriority(priority);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Lowers the priority for all regionfile types for the specified chunk.
|
|
- *
|
|
- * @param world Specified world.
|
|
- * @param chunkX Specified chunk x.
|
|
- * @param chunkZ Specified chunk z.
|
|
- * @param priority New priority.
|
|
- *
|
|
- * @see #raisePriority(ServerLevel, int, int, Priority)
|
|
- * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- * @see #setPriority(ServerLevel, int, int, Priority)
|
|
- * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- */
|
|
- public static void lowerPriority(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final Priority priority) {
|
|
- for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
|
|
- RegionFileIOThread.lowerPriority(world, chunkX, chunkZ, type, priority);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Lowers the priority for the specified regionfile type for the specified chunk.
|
|
- *
|
|
- * @param world Specified world.
|
|
- * @param chunkX Specified chunk x.
|
|
- * @param chunkZ Specified chunk z.
|
|
- * @param type Specified regionfile type.
|
|
- * @param priority New priority.
|
|
- *
|
|
- * @see #raisePriority(ServerLevel, int, int, Priority)
|
|
- * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- * @see #setPriority(ServerLevel, int, int, Priority)
|
|
- * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
|
|
- */
|
|
- public static void lowerPriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
- final Priority priority) {
|
|
- final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
|
|
- thread.lowerPriorityInternal(world, chunkX, chunkZ, type, priority);
|
|
- }
|
|
-
|
|
- void lowerPriorityInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
- final Priority priority) {
|
|
- final ChunkDataController taskController = getControllerFor(world, type);
|
|
- final ChunkDataTask task = taskController.tasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
-
|
|
- if (task != null) {
|
|
- task.prioritisedTask.lowerPriority(priority);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Schedules the chunk data to be written asynchronously.
|
|
- * <p>
|
|
- * Impl notes:
|
|
- * </p>
|
|
- * <li>
|
|
- * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
|
|
- * saves must be scheduled before a chunk is unloaded.
|
|
- * </li>
|
|
- * <li>
|
|
- * Writes may be called concurrently, although only the "later" write will go through.
|
|
- * </li>
|
|
- *
|
|
- * @param world Chunk's world
|
|
- * @param chunkX Chunk's x coordinate
|
|
- * @param chunkZ Chunk's z coordinate
|
|
- * @param data Chunk's data
|
|
- * @param type The regionfile type to write to.
|
|
- *
|
|
- * @throws IllegalStateException If the file io thread has shutdown.
|
|
- */
|
|
- public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
|
|
- final RegionFileType type) {
|
|
- RegionFileIOThread.scheduleSave(world, chunkX, chunkZ, data, type, Priority.NORMAL);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Schedules the chunk data to be written asynchronously.
|
|
- * <p>
|
|
- * Impl notes:
|
|
- * </p>
|
|
- * <li>
|
|
- * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
|
|
- * saves must be scheduled before a chunk is unloaded.
|
|
- * </li>
|
|
- * <li>
|
|
- * Writes may be called concurrently, although only the "later" write will go through.
|
|
- * </li>
|
|
- *
|
|
- * @param world Chunk's world
|
|
- * @param chunkX Chunk's x coordinate
|
|
- * @param chunkZ Chunk's z coordinate
|
|
- * @param data Chunk's data
|
|
- * @param type The regionfile type to write to.
|
|
- * @param priority The minimum priority to schedule at.
|
|
- *
|
|
- * @throws IllegalStateException If the file io thread has shutdown.
|
|
- */
|
|
- public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
|
|
- final RegionFileType type, final Priority priority) {
|
|
- final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
|
|
- thread.scheduleSaveInternal(world, chunkX, chunkZ, data, type, priority);
|
|
- }
|
|
-
|
|
- void scheduleSaveInternal(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
|
|
- final RegionFileType type, final Priority priority) {
|
|
- final ChunkDataController taskController = getControllerFor(world, type);
|
|
-
|
|
- final boolean[] created = new boolean[1];
|
|
- final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
|
- final ChunkDataTask task = taskController.tasks.compute(key, (final long keyInMap, final ChunkDataTask taskRunning) -> {
|
|
- if (taskRunning == null || taskRunning.failedWrite) {
|
|
- // no task is scheduled or the previous write failed - meaning we need to overwrite it
|
|
-
|
|
- // create task
|
|
- final ChunkDataTask newTask = new ChunkDataTask(world, chunkX, chunkZ, taskController, RegionFileIOThread.this, priority);
|
|
- newTask.inProgressWrite = data;
|
|
- created[0] = true;
|
|
-
|
|
- return newTask;
|
|
- }
|
|
-
|
|
- taskRunning.inProgressWrite = data;
|
|
-
|
|
- return taskRunning;
|
|
- });
|
|
-
|
|
- if (created[0]) {
|
|
- task.prioritisedTask.queue();
|
|
- } else {
|
|
- task.prioritisedTask.raisePriority(priority);
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * Schedules a load to be executed asynchronously. This task will load all regionfile types, and then call
|
|
- * {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)}
|
|
- * for single load.
|
|
- * <p>
|
|
- * Impl notes:
|
|
- * </p>
|
|
- * <li>
|
|
- * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
- * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
- * data is undefined behaviour, and can cause deadlock.
|
|
- * </li>
|
|
- *
|
|
- * @param world Chunk's world
|
|
- * @param chunkX Chunk's x coordinate
|
|
- * @param chunkZ Chunk's z coordinate
|
|
- * @param onComplete Consumer to execute once this task has completed
|
|
- * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
- * of this call.
|
|
- *
|
|
- * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
- *
|
|
- * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
|
|
- * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
|
|
- * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
|
|
- * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
|
|
- */
|
|
- public static Cancellable loadAllChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final Consumer<RegionFileData> onComplete, final boolean intendingToBlock) {
|
|
- return RegionFileIOThread.loadAllChunkData(world, chunkX, chunkZ, onComplete, intendingToBlock, Priority.NORMAL);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Schedules a load to be executed asynchronously. This task will load all regionfile types, and then call
|
|
- * {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)}
|
|
- * for single load.
|
|
- * <p>
|
|
- * Impl notes:
|
|
- * </p>
|
|
- * <li>
|
|
- * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
- * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
- * data is undefined behaviour, and can cause deadlock.
|
|
- * </li>
|
|
- *
|
|
- * @param world Chunk's world
|
|
- * @param chunkX Chunk's x coordinate
|
|
- * @param chunkZ Chunk's z coordinate
|
|
- * @param onComplete Consumer to execute once this task has completed
|
|
- * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
- * of this call.
|
|
- * @param priority The minimum priority to load the data at.
|
|
- *
|
|
- * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
- *
|
|
- * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
|
|
- * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
|
|
- * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
|
|
- * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
|
|
- */
|
|
- public static Cancellable loadAllChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final Consumer<RegionFileData> onComplete, final boolean intendingToBlock,
|
|
- final Priority priority) {
|
|
- return RegionFileIOThread.loadChunkData(world, chunkX, chunkZ, onComplete, intendingToBlock, priority, CACHED_REGIONFILE_TYPES);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Schedules a load to be executed asynchronously. This task will load data for the specified regionfile type(s), and
|
|
- * then call {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)}
|
|
- * for single load.
|
|
- * <p>
|
|
- * Impl notes:
|
|
- * </p>
|
|
- * <li>
|
|
- * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
- * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
- * data is undefined behaviour, and can cause deadlock.
|
|
- * </li>
|
|
- *
|
|
- * @param world Chunk's world
|
|
- * @param chunkX Chunk's x coordinate
|
|
- * @param chunkZ Chunk's z coordinate
|
|
- * @param onComplete Consumer to execute once this task has completed
|
|
- * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
- * of this call.
|
|
- * @param types The regionfile type(s) to load.
|
|
- *
|
|
- * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
- *
|
|
- * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
|
|
- * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
|
|
- * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
|
|
- * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
|
|
- */
|
|
- public static Cancellable loadChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final Consumer<RegionFileData> onComplete, final boolean intendingToBlock,
|
|
- final RegionFileType... types) {
|
|
- return RegionFileIOThread.loadChunkData(world, chunkX, chunkZ, onComplete, intendingToBlock, Priority.NORMAL, types);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Schedules a load to be executed asynchronously. This task will load data for the specified regionfile type(s), and
|
|
- * then call {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)}
|
|
- * for single load.
|
|
- * <p>
|
|
- * Impl notes:
|
|
- * </p>
|
|
- * <li>
|
|
- * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
- * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
- * data is undefined behaviour, and can cause deadlock.
|
|
- * </li>
|
|
- *
|
|
- * @param world Chunk's world
|
|
- * @param chunkX Chunk's x coordinate
|
|
- * @param chunkZ Chunk's z coordinate
|
|
- * @param onComplete Consumer to execute once this task has completed
|
|
- * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
- * of this call.
|
|
- * @param types The regionfile type(s) to load.
|
|
- * @param priority The minimum priority to load the data at.
|
|
- *
|
|
- * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
- *
|
|
- * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
|
|
- * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
|
|
- * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
|
|
- * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
|
|
- */
|
|
- public static Cancellable loadChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final Consumer<RegionFileData> onComplete, final boolean intendingToBlock,
|
|
- final Priority priority, final RegionFileType... types) {
|
|
- if (types == null) {
|
|
- throw new NullPointerException("Types cannot be null");
|
|
- }
|
|
- if (types.length == 0) {
|
|
- throw new IllegalArgumentException("Types cannot be empty");
|
|
- }
|
|
-
|
|
- final RegionFileData ret = new RegionFileData();
|
|
-
|
|
- final Cancellable[] reads = new CancellableRead[types.length];
|
|
- final AtomicInteger completions = new AtomicInteger();
|
|
- final int expectedCompletions = types.length;
|
|
-
|
|
- for (int i = 0; i < expectedCompletions; ++i) {
|
|
- final RegionFileType type = types[i];
|
|
- reads[i] = RegionFileIOThread.loadDataAsync(world, chunkX, chunkZ, type,
|
|
- (final CompoundTag data, final Throwable throwable) -> {
|
|
- if (throwable != null) {
|
|
- ret.setThrowable(type, throwable);
|
|
- } else {
|
|
- ret.setData(type, data);
|
|
- }
|
|
-
|
|
- if (completions.incrementAndGet() == expectedCompletions) {
|
|
- onComplete.accept(ret);
|
|
- }
|
|
- }, intendingToBlock, priority);
|
|
- }
|
|
-
|
|
- return new CancellableReads(reads);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Schedules a load to be executed asynchronously. This task will load the specified regionfile type, and then call
|
|
- * {@code onComplete}.
|
|
- * <p>
|
|
- * Impl notes:
|
|
- * </p>
|
|
- * <li>
|
|
- * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
- * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
- * data is undefined behaviour, and can cause deadlock.
|
|
- * </li>
|
|
- *
|
|
- * @param world Chunk's world
|
|
- * @param chunkX Chunk's x coordinate
|
|
- * @param chunkZ Chunk's z coordinate
|
|
- * @param onComplete Consumer to execute once this task has completed
|
|
- * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
- * of this call.
|
|
- *
|
|
- * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
- *
|
|
- * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
|
|
- * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
|
|
- * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
|
|
- * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
|
|
- */
|
|
- public static Cancellable loadDataAsync(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final RegionFileType type, final BiConsumer<CompoundTag, Throwable> onComplete,
|
|
- final boolean intendingToBlock) {
|
|
- return RegionFileIOThread.loadDataAsync(world, chunkX, chunkZ, type, onComplete, intendingToBlock, Priority.NORMAL);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Schedules a load to be executed asynchronously. This task will load the specified regionfile type, and then call
|
|
- * {@code onComplete}.
|
|
- * <p>
|
|
- * Impl notes:
|
|
- * </p>
|
|
- * <li>
|
|
- * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
|
|
- * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
|
|
- * data is undefined behaviour, and can cause deadlock.
|
|
- * </li>
|
|
- *
|
|
- * @param world Chunk's world
|
|
- * @param chunkX Chunk's x coordinate
|
|
- * @param chunkZ Chunk's z coordinate
|
|
- * @param onComplete Consumer to execute once this task has completed
|
|
- * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
|
|
- * of this call.
|
|
- * @param priority Minimum priority to load the data at.
|
|
- *
|
|
- * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
|
|
- *
|
|
- * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
|
|
- * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
|
|
- * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
|
|
- * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
|
|
- */
|
|
- public static Cancellable loadDataAsync(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final RegionFileType type, final BiConsumer<CompoundTag, Throwable> onComplete,
|
|
- final boolean intendingToBlock, final Priority priority) {
|
|
- final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
|
|
- return thread.loadDataAsyncInternal(world, chunkX, chunkZ, type, onComplete, intendingToBlock, priority);
|
|
- }
|
|
-
|
|
- Cancellable loadDataAsyncInternal(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final RegionFileType type, final BiConsumer<CompoundTag, Throwable> onComplete,
|
|
- final boolean intendingToBlock, final Priority priority) {
|
|
- final ChunkDataController taskController = getControllerFor(world, type);
|
|
-
|
|
- final ImmediateCallbackCompletion callbackInfo = new ImmediateCallbackCompletion();
|
|
-
|
|
- final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
|
- final BiLong1Function<ChunkDataTask, ChunkDataTask> compute = (final long keyInMap, final ChunkDataTask running) -> {
|
|
- if (running == null) {
|
|
- // not scheduled
|
|
-
|
|
- // set up task
|
|
- final ChunkDataTask newTask = new ChunkDataTask(
|
|
- world, chunkX, chunkZ, taskController, RegionFileIOThread.this, priority
|
|
- );
|
|
- newTask.inProgressRead = new InProgressRead();
|
|
- newTask.inProgressRead.addToAsyncWaiters(onComplete);
|
|
-
|
|
- callbackInfo.tasksNeedsScheduling = true;
|
|
- return newTask;
|
|
- }
|
|
-
|
|
- final CompoundTag pendingWrite = running.inProgressWrite;
|
|
-
|
|
- if (pendingWrite == ChunkDataTask.NOTHING_TO_WRITE) {
|
|
- // need to add to waiters here, because the regionfile thread will use compute() to lock and check for cancellations
|
|
- if (!running.inProgressRead.addToAsyncWaiters(onComplete)) {
|
|
- callbackInfo.data = running.inProgressRead.value;
|
|
- callbackInfo.throwable = running.inProgressRead.throwable;
|
|
- callbackInfo.completeNow = true;
|
|
- }
|
|
- return running;
|
|
- }
|
|
-
|
|
- // at this stage we have to use the in progress write's data to avoid an order issue
|
|
- callbackInfo.data = pendingWrite;
|
|
- callbackInfo.throwable = null;
|
|
- callbackInfo.completeNow = true;
|
|
- return running;
|
|
- };
|
|
-
|
|
- final ChunkDataTask ret = taskController.tasks.compute(key, compute);
|
|
-
|
|
- // needs to be scheduled
|
|
- if (callbackInfo.tasksNeedsScheduling) {
|
|
- ret.prioritisedTask.queue();
|
|
- } else if (callbackInfo.completeNow) {
|
|
- try {
|
|
- onComplete.accept(callbackInfo.data == null ? null : callbackInfo.data.copy(), callbackInfo.throwable);
|
|
- } catch (final Throwable thr) {
|
|
- LOGGER.error("Callback " + ConcurrentUtil.genericToString(onComplete) + " synchronously failed to handle chunk data for task " + ret.toString(), thr);
|
|
- }
|
|
- } else {
|
|
- // we're waiting on a task we didn't schedule, so raise its priority to what we want
|
|
- ret.prioritisedTask.raisePriority(priority);
|
|
- }
|
|
-
|
|
- return new CancellableRead(onComplete, ret);
|
|
- }
|
|
-
|
|
- /**
|
|
- * Schedules a load task to be executed asynchronously, and blocks on that task.
|
|
- *
|
|
- * @param world Chunk's world
|
|
- * @param chunkX Chunk's x coordinate
|
|
- * @param chunkZ Chunk's z coordinate
|
|
- * @param type Regionfile type
|
|
- * @param priority Minimum priority to load the data at.
|
|
- *
|
|
- * @return The chunk data for the chunk. Note that a {@code null} result means the chunk or regionfile does not exist on disk.
|
|
- *
|
|
- * @throws IOException If the load fails for any reason
|
|
- */
|
|
- public static CompoundTag loadData(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
|
|
- final Priority priority) throws IOException {
|
|
- final CompletableFuture<CompoundTag> ret = new CompletableFuture<>();
|
|
-
|
|
- RegionFileIOThread.loadDataAsync(world, chunkX, chunkZ, type, (final CompoundTag compound, final Throwable thr) -> {
|
|
- if (thr != null) {
|
|
- ret.completeExceptionally(thr);
|
|
- } else {
|
|
- ret.complete(compound);
|
|
- }
|
|
- }, true, priority);
|
|
-
|
|
- try {
|
|
- return ret.join();
|
|
- } catch (final CompletionException ex) {
|
|
- throw new IOException(ex);
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class ImmediateCallbackCompletion {
|
|
-
|
|
- public CompoundTag data;
|
|
- public Throwable throwable;
|
|
- public boolean completeNow;
|
|
- public boolean tasksNeedsScheduling;
|
|
-
|
|
- }
|
|
-
|
|
- private static final class CancellableRead implements Cancellable {
|
|
-
|
|
- private BiConsumer<CompoundTag, Throwable> callback;
|
|
- private ChunkDataTask task;
|
|
-
|
|
- CancellableRead(final BiConsumer<CompoundTag, Throwable> callback, final ChunkDataTask task) {
|
|
- this.callback = callback;
|
|
- this.task = task;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean cancel() {
|
|
- final BiConsumer<CompoundTag, Throwable> callback = this.callback;
|
|
- final ChunkDataTask task = this.task;
|
|
-
|
|
- if (callback == null || task == null) {
|
|
- return false;
|
|
- }
|
|
-
|
|
- this.callback = null;
|
|
- this.task = null;
|
|
-
|
|
- final InProgressRead read = task.inProgressRead;
|
|
-
|
|
- // read can be null if no read was scheduled (i.e no regionfile existed or chunk in regionfile didn't)
|
|
- return read != null && read.cancel(callback);
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class CancellableReads implements Cancellable {
|
|
-
|
|
- private Cancellable[] reads;
|
|
-
|
|
- private static final VarHandle READS_HANDLE = ConcurrentUtil.getVarHandle(CancellableReads.class, "reads", Cancellable[].class);
|
|
-
|
|
- CancellableReads(final Cancellable[] reads) {
|
|
- this.reads = reads;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean cancel() {
|
|
- final Cancellable[] reads = (Cancellable[])READS_HANDLE.getAndSet((CancellableReads)this, (Cancellable[])null);
|
|
-
|
|
- if (reads == null) {
|
|
- return false;
|
|
- }
|
|
-
|
|
- boolean ret = false;
|
|
-
|
|
- for (final Cancellable read : reads) {
|
|
- ret |= read.cancel();
|
|
- }
|
|
-
|
|
- return ret;
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class InProgressRead {
|
|
-
|
|
- private static final Logger LOGGER = LoggerFactory.getLogger(InProgressRead.class);
|
|
-
|
|
- private CompoundTag value;
|
|
- private Throwable throwable;
|
|
- private final MultiThreadedQueue<BiConsumer<CompoundTag, Throwable>> callbacks = new MultiThreadedQueue<>();
|
|
-
|
|
- public boolean hasNoWaiters() {
|
|
- return this.callbacks.isEmpty();
|
|
- }
|
|
-
|
|
- public boolean addToAsyncWaiters(final BiConsumer<CompoundTag, Throwable> callback) {
|
|
- return this.callbacks.add(callback);
|
|
- }
|
|
-
|
|
- public boolean cancel(final BiConsumer<CompoundTag, Throwable> callback) {
|
|
- return this.callbacks.remove(callback);
|
|
- }
|
|
-
|
|
- public void complete(final ChunkDataTask task, final CompoundTag value, final Throwable throwable) {
|
|
- this.value = value;
|
|
- this.throwable = throwable;
|
|
-
|
|
- BiConsumer<CompoundTag, Throwable> consumer;
|
|
- while ((consumer = this.callbacks.pollOrBlockAdds()) != null) {
|
|
- try {
|
|
- consumer.accept(value == null ? null : value.copy(), throwable);
|
|
- } catch (final Throwable thr) {
|
|
- LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data for task " + task.toString(), thr);
|
|
- }
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- public static abstract class ChunkDataController {
|
|
-
|
|
- // ConcurrentHashMap synchronizes per chain, so reduce the chance of task's hashes colliding.
|
|
- private final ConcurrentLong2ReferenceChainedHashTable<ChunkDataTask> tasks = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(8192, 0.5f);
|
|
-
|
|
- public final RegionFileType type;
|
|
-
|
|
- public ChunkDataController(final RegionFileType type) {
|
|
- this.type = type;
|
|
- }
|
|
-
|
|
- public abstract RegionFileStorage getCache();
|
|
-
|
|
- public abstract void writeData(final int chunkX, final int chunkZ, final CompoundTag compound) throws IOException;
|
|
-
|
|
- public abstract CompoundTag readData(final int chunkX, final int chunkZ) throws IOException;
|
|
-
|
|
- public boolean hasTasks() {
|
|
- return !this.tasks.isEmpty();
|
|
- }
|
|
-
|
|
- public boolean doesRegionFileNotExist(final int chunkX, final int chunkZ) {
|
|
- return ((ChunkSystemRegionFileStorage)(Object)this.getCache()).moonrise$doesRegionFileNotExistNoIO(chunkX, chunkZ);
|
|
- }
|
|
-
|
|
- public <T> T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function<RegionFile, T> function) {
|
|
- final RegionFileStorage cache = this.getCache();
|
|
- final RegionFile regionFile;
|
|
- synchronized (cache) {
|
|
- try {
|
|
- if (existingOnly) {
|
|
- regionFile = ((ChunkSystemRegionFileStorage)(Object)cache).moonrise$getRegionFileIfExists(chunkX, chunkZ);
|
|
- } else {
|
|
- regionFile = cache.getRegionFile(new ChunkPos(chunkX, chunkZ), existingOnly);
|
|
- }
|
|
- } catch (final IOException ex) {
|
|
- throw new RuntimeException(ex);
|
|
- }
|
|
-
|
|
- return function.apply(regionFile);
|
|
- }
|
|
- }
|
|
-
|
|
- public <T> T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function<RegionFile, T> function) {
|
|
- final RegionFileStorage cache = this.getCache();
|
|
- final RegionFile regionFile;
|
|
-
|
|
- synchronized (cache) {
|
|
- regionFile = ((ChunkSystemRegionFileStorage)(Object)cache).moonrise$getRegionFileIfLoaded(chunkX, chunkZ);
|
|
-
|
|
- return function.apply(regionFile);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- private static final class ChunkDataTask implements Runnable {
|
|
-
|
|
- private static final CompoundTag NOTHING_TO_WRITE = new CompoundTag();
|
|
-
|
|
- private static final Logger LOGGER = LoggerFactory.getLogger(ChunkDataTask.class);
|
|
-
|
|
- private InProgressRead inProgressRead;
|
|
- private volatile CompoundTag inProgressWrite = NOTHING_TO_WRITE; // only needs to be acquire/release
|
|
-
|
|
- private boolean failedWrite;
|
|
-
|
|
- private final ServerLevel world;
|
|
- private final int chunkX;
|
|
- private final int chunkZ;
|
|
- private final ChunkDataController taskController;
|
|
-
|
|
- private final PrioritisedTask prioritisedTask;
|
|
-
|
|
- /*
|
|
- * IO thread will perform reads before writes for a given chunk x and z
|
|
- *
|
|
- * How reads/writes are scheduled:
|
|
- *
|
|
- * If read is scheduled while scheduling write, take no special action and just schedule write
|
|
- * If read is scheduled while scheduling read and no write is scheduled, chain the read task
|
|
- *
|
|
- *
|
|
- * If write is scheduled while scheduling read, use the pending write data and ret immediately (so no read is scheduled)
|
|
- * If write is scheduled while scheduling write (ignore read in progress), overwrite the write in progress data
|
|
- *
|
|
- * This allows the reads and writes to act as if they occur synchronously to the thread scheduling them, however
|
|
- * it fails to properly propagate write failures thanks to writes overwriting each other
|
|
- */
|
|
-
|
|
- public ChunkDataTask(final ServerLevel world, final int chunkX, final int chunkZ, final ChunkDataController taskController,
|
|
- final PrioritisedExecutor executor, final Priority priority) {
|
|
- this.world = world;
|
|
- this.chunkX = chunkX;
|
|
- this.chunkZ = chunkZ;
|
|
- this.taskController = taskController;
|
|
- this.prioritisedTask = executor.createTask(this, priority);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String toString() {
|
|
- return "Task for world: '" + WorldUtil.getWorldName(this.world) + "' at (" + this.chunkX + "," + this.chunkZ +
|
|
- ") type: " + this.taskController.type.name() + ", hash: " + this.hashCode();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void run() {
|
|
- final InProgressRead read = this.inProgressRead;
|
|
- final long chunkKey = CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ);
|
|
-
|
|
- if (read != null) {
|
|
- final boolean[] canRead = new boolean[] { true };
|
|
-
|
|
- if (read.hasNoWaiters()) {
|
|
- // cancelled read? go to task controller to confirm
|
|
- final ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final long keyInMap, final ChunkDataTask valueInMap) -> {
|
|
- if (valueInMap == null) {
|
|
- throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!");
|
|
- }
|
|
- if (valueInMap != ChunkDataTask.this) {
|
|
- throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
|
|
- }
|
|
-
|
|
- if (!read.hasNoWaiters()) {
|
|
- return valueInMap;
|
|
- } else {
|
|
- canRead[0] = false;
|
|
- }
|
|
-
|
|
- return valueInMap.inProgressWrite == NOTHING_TO_WRITE ? null : valueInMap;
|
|
- });
|
|
-
|
|
- if (inMap == null) {
|
|
- // read is cancelled - and no write pending, so we're done
|
|
- return;
|
|
- }
|
|
- // if there is a write in progress, we don't actually have to worry about waiters gaining new entries -
|
|
- // the readers will just use the in progress write, so the value in canRead is good to use without
|
|
- // further synchronisation.
|
|
- }
|
|
-
|
|
- if (canRead[0]) {
|
|
- CompoundTag compound = null;
|
|
- Throwable throwable = null;
|
|
-
|
|
- try {
|
|
- compound = this.taskController.readData(this.chunkX, this.chunkZ);
|
|
- } catch (final Throwable thr) {
|
|
- throwable = thr;
|
|
- LOGGER.error("Failed to read chunk data for task: " + this.toString(), thr);
|
|
- }
|
|
- read.complete(this, compound, throwable);
|
|
- }
|
|
- }
|
|
-
|
|
- CompoundTag write = this.inProgressWrite;
|
|
-
|
|
- if (write == NOTHING_TO_WRITE) {
|
|
- final ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final long keyInMap, final ChunkDataTask valueInMap) -> {
|
|
- if (valueInMap == null) {
|
|
- throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!");
|
|
- }
|
|
- if (valueInMap != ChunkDataTask.this) {
|
|
- throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
|
|
- }
|
|
- return valueInMap.inProgressWrite == NOTHING_TO_WRITE ? null : valueInMap;
|
|
- });
|
|
-
|
|
- if (inMap == null) {
|
|
- return; // set the task value to null, indicating we're done
|
|
- } // else: inProgressWrite changed, so now we have something to write
|
|
- }
|
|
-
|
|
- for (;;) {
|
|
- write = this.inProgressWrite;
|
|
- final CompoundTag dataWritten = write;
|
|
-
|
|
- boolean failedWrite = false;
|
|
-
|
|
- try {
|
|
- this.taskController.writeData(this.chunkX, this.chunkZ, write);
|
|
- } catch (final Throwable thr) {
|
|
- if (thr instanceof RegionFileStorage.RegionFileSizeException) {
|
|
- final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024);
|
|
- LOGGER.error("Chunk at (" + this.chunkX + "," + this.chunkZ + ") in '" + WorldUtil.getWorldName(this.world) + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk.");
|
|
- } else {
|
|
- failedWrite = thr instanceof IOException;
|
|
- LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
|
|
- }
|
|
- }
|
|
-
|
|
- final boolean finalFailWrite = failedWrite;
|
|
- final boolean[] done = new boolean[] { false };
|
|
-
|
|
- this.taskController.tasks.compute(chunkKey, (final long keyInMap, final ChunkDataTask valueInMap) -> {
|
|
- if (valueInMap == null) {
|
|
- throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!");
|
|
- }
|
|
- if (valueInMap != ChunkDataTask.this) {
|
|
- throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
|
|
- }
|
|
- if (valueInMap.inProgressWrite == dataWritten) {
|
|
- valueInMap.failedWrite = finalFailWrite;
|
|
- done[0] = true;
|
|
- // keep the data in map if we failed the write so we can try to prevent data loss
|
|
- return finalFailWrite ? valueInMap : null;
|
|
- }
|
|
- // different data than expected, means we need to retry write
|
|
- return valueInMap;
|
|
- });
|
|
-
|
|
- if (done[0]) {
|
|
- return;
|
|
- }
|
|
-
|
|
- // fetch & write new data
|
|
- continue;
|
|
- }
|
|
- }
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/ChunkDataController.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/ChunkDataController.java
|
|
index c35e0c29700be48dda3e53e7d2db224766ef17b7..a36ab89f5c37f5f9ab0152f087bb4cf3560f8581 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/ChunkDataController.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/ChunkDataController.java
|
|
@@ -1,22 +1,24 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller;
|
|
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemChunkMap;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkStorage;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
|
import java.io.IOException;
|
|
-import java.util.Optional;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.CompletionException;
|
|
|
|
-public final class ChunkDataController extends RegionFileIOThread.ChunkDataController {
|
|
+public final class ChunkDataController extends MoonriseRegionFileIO.RegionDataController {
|
|
|
|
private final ServerLevel world;
|
|
|
|
- public ChunkDataController(final ServerLevel world) {
|
|
- super(RegionFileIOThread.RegionFileType.CHUNK_DATA);
|
|
+ public ChunkDataController(final ServerLevel world, final ChunkTaskScheduler taskScheduler) {
|
|
+ super(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, taskScheduler.ioExecutor, taskScheduler.compressionExecutor);
|
|
this.world = world;
|
|
}
|
|
|
|
@@ -26,31 +28,23 @@ public final class ChunkDataController extends RegionFileIOThread.ChunkDataContr
|
|
}
|
|
|
|
@Override
|
|
- public void writeData(final int chunkX, final int chunkZ, final CompoundTag compound) throws IOException {
|
|
- final CompletableFuture<Void> future = this.world.getChunkSource().chunkMap.write(new ChunkPos(chunkX, chunkZ), compound);
|
|
-
|
|
- try {
|
|
- if (future != null) {
|
|
- // rets non-null when sync writing (i.e. future should be completed here)
|
|
- future.join();
|
|
- }
|
|
- } catch (final CompletionException ex) {
|
|
- if (ex.getCause() instanceof IOException ioException) {
|
|
- throw ioException;
|
|
- }
|
|
- throw ex;
|
|
- }
|
|
+ public WriteData startWrite(final int chunkX, final int chunkZ, final CompoundTag compound) throws IOException {
|
|
+ return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$startWrite(chunkX, chunkZ, compound);
|
|
}
|
|
|
|
@Override
|
|
- public CompoundTag readData(final int chunkX, final int chunkZ) throws IOException {
|
|
- try {
|
|
- return this.world.getChunkSource().chunkMap.read(new ChunkPos(chunkX, chunkZ)).join().orElse(null);
|
|
- } catch (final CompletionException ex) {
|
|
- if (ex.getCause() instanceof IOException ioException) {
|
|
- throw ioException;
|
|
- }
|
|
- throw ex;
|
|
- }
|
|
+ public void finishWrite(final int chunkX, final int chunkZ, final WriteData writeData) throws IOException {
|
|
+ ((ChunkSystemChunkMap)this.world.getChunkSource().chunkMap).moonrise$writeFinishCallback(new ChunkPos(chunkX, chunkZ));
|
|
+ ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$finishWrite(chunkX, chunkZ, writeData);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ReadData readData(final int chunkX, final int chunkZ) throws IOException {
|
|
+ return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$readData(chunkX, chunkZ);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public CompoundTag finishRead(final int chunkX, final int chunkZ, final ReadData readData) throws IOException {
|
|
+ return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$finishRead(chunkX, chunkZ, readData);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/EntityDataController.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/EntityDataController.java
|
|
index fdd189ef056187941d43809c5d61cab717aecf60..828c868f68c2a20bf90d0f7ec253fdeb591f15f6 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/EntityDataController.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/EntityDataController.java
|
|
@@ -1,6 +1,8 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller;
|
|
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.chunk.storage.EntityStorage;
|
|
@@ -9,12 +11,12 @@ import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
|
|
import java.io.IOException;
|
|
import java.nio.file.Path;
|
|
|
|
-public final class EntityDataController extends RegionFileIOThread.ChunkDataController {
|
|
+public final class EntityDataController extends MoonriseRegionFileIO.RegionDataController {
|
|
|
|
private final EntityRegionFileStorage storage;
|
|
|
|
- public EntityDataController(final EntityRegionFileStorage storage) {
|
|
- super(RegionFileIOThread.RegionFileType.ENTITY_DATA);
|
|
+ public EntityDataController(final EntityRegionFileStorage storage, final ChunkTaskScheduler taskScheduler) {
|
|
+ super(MoonriseRegionFileIO.RegionFileType.ENTITY_DATA, taskScheduler.ioExecutor, taskScheduler.compressionExecutor);
|
|
this.storage = storage;
|
|
}
|
|
|
|
@@ -24,13 +26,35 @@ public final class EntityDataController extends RegionFileIOThread.ChunkDataCont
|
|
}
|
|
|
|
@Override
|
|
- public void writeData(final int chunkX, final int chunkZ, final CompoundTag compound) throws IOException {
|
|
- this.storage.write(new ChunkPos(chunkX, chunkZ), compound);
|
|
+ public WriteData startWrite(final int chunkX, final int chunkZ, final CompoundTag compound) throws IOException {
|
|
+ checkPosition(new ChunkPos(chunkX, chunkZ), compound);
|
|
+
|
|
+ return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$startWrite(chunkX, chunkZ, compound);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void finishWrite(final int chunkX, final int chunkZ, final WriteData writeData) throws IOException {
|
|
+ ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$finishWrite(chunkX, chunkZ, writeData);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ReadData readData(final int chunkX, final int chunkZ) throws IOException {
|
|
+ return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$readData(chunkX, chunkZ);
|
|
}
|
|
|
|
@Override
|
|
- public CompoundTag readData(final int chunkX, final int chunkZ) throws IOException {
|
|
- return this.storage.read(new ChunkPos(chunkX, chunkZ));
|
|
+ public CompoundTag finishRead(final int chunkX, final int chunkZ, final ReadData readData) throws IOException {
|
|
+ return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$finishRead(chunkX, chunkZ, readData);
|
|
+ }
|
|
+
|
|
+ private static void checkPosition(final ChunkPos pos, final CompoundTag nbt) {
|
|
+ final ChunkPos nbtPos = nbt == null ? null : EntityStorage.readChunkPos(nbt);
|
|
+ if (nbtPos != null && !pos.equals(nbtPos)) {
|
|
+ throw new IllegalArgumentException(
|
|
+ "Entity chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos.toString()
|
|
+ + " but compound says coordinate is " + nbtPos
|
|
+ );
|
|
+ }
|
|
}
|
|
|
|
public static final class EntityRegionFileStorage extends RegionFileStorage {
|
|
@@ -42,13 +66,7 @@ public final class EntityDataController extends RegionFileIOThread.ChunkDataCont
|
|
|
|
@Override
|
|
public void write(final ChunkPos pos, final CompoundTag nbt) throws IOException {
|
|
- final ChunkPos nbtPos = nbt == null ? null : EntityStorage.readChunkPos(nbt);
|
|
- if (nbtPos != null && !pos.equals(nbtPos)) {
|
|
- throw new IllegalArgumentException(
|
|
- "Entity chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos.toString()
|
|
- + " but compound says coordinate is " + nbtPos + " for world: " + this
|
|
- );
|
|
- }
|
|
+ checkPosition(pos, nbt);
|
|
super.write(pos, nbt);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/PoiDataController.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/PoiDataController.java
|
|
index af867f8fedd0bb8f675e94243aa1a3f17363483b..bd0d782852f9cfe5bc0b5339ecf4d82c10332ec9 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/PoiDataController.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/datacontroller/PoiDataController.java
|
|
@@ -1,18 +1,20 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.io.datacontroller;
|
|
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.ChunkSystemRegionFileStorage;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.storage.ChunkSystemSectionStorage;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
|
import java.io.IOException;
|
|
|
|
-public final class PoiDataController extends RegionFileIOThread.ChunkDataController {
|
|
+public final class PoiDataController extends MoonriseRegionFileIO.RegionDataController {
|
|
|
|
private final ServerLevel world;
|
|
|
|
- public PoiDataController(final ServerLevel world) {
|
|
- super(RegionFileIOThread.RegionFileType.POI_DATA);
|
|
+ public PoiDataController(final ServerLevel world, final ChunkTaskScheduler taskScheduler) {
|
|
+ super(MoonriseRegionFileIO.RegionFileType.POI_DATA, taskScheduler.ioExecutor, taskScheduler.compressionExecutor);
|
|
this.world = world;
|
|
}
|
|
|
|
@@ -22,12 +24,22 @@ public final class PoiDataController extends RegionFileIOThread.ChunkDataControl
|
|
}
|
|
|
|
@Override
|
|
- public void writeData(final int chunkX, final int chunkZ, final CompoundTag compound) throws IOException {
|
|
- ((ChunkSystemSectionStorage)this.world.getPoiManager()).moonrise$write(chunkX, chunkZ, compound);
|
|
+ public WriteData startWrite(final int chunkX, final int chunkZ, final CompoundTag compound) throws IOException {
|
|
+ return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$startWrite(chunkX, chunkZ, compound);
|
|
}
|
|
|
|
@Override
|
|
- public CompoundTag readData(final int chunkX, final int chunkZ) throws IOException {
|
|
- return ((ChunkSystemSectionStorage)this.world.getPoiManager()).moonrise$read(chunkX, chunkZ);
|
|
+ public void finishWrite(final int chunkX, final int chunkZ, final WriteData writeData) throws IOException {
|
|
+ ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$finishWrite(chunkX, chunkZ, writeData);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public ReadData readData(final int chunkX, final int chunkZ) throws IOException {
|
|
+ return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$readData(chunkX, chunkZ);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public CompoundTag finishRead(final int chunkX, final int chunkZ, final ReadData readData) throws IOException {
|
|
+ return ((ChunkSystemRegionFileStorage)this.getCache()).moonrise$finishRead(chunkX, chunkZ, readData);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemChunkMap.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemChunkMap.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..47a4d3376d08dde94a39254bec21473ff27f53e6
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemChunkMap.java
|
|
@@ -0,0 +1,10 @@
|
|
+package ca.spottedleaf.moonrise.patches.chunk_system.level;
|
|
+
|
|
+import net.minecraft.world.level.ChunkPos;
|
|
+import java.io.IOException;
|
|
+
|
|
+public interface ChunkSystemChunkMap {
|
|
+
|
|
+ public void moonrise$writeFinishCallback(final ChunkPos pos) throws IOException;
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java
|
|
index efcd9057f008f0b9cf0d22b2b21d1851205841e5..5d4d650186b18eb00782429d53d861564d8e4ba9 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemLevel.java
|
|
@@ -1,5 +1,6 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.level;
|
|
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.LevelChunk;
|
|
@@ -19,4 +20,14 @@ public interface ChunkSystemLevel {
|
|
|
|
public void moonrise$midTickTasks();
|
|
|
|
+ public ChunkData moonrise$getChunkData(final long chunkKey);
|
|
+
|
|
+ public ChunkData moonrise$getChunkData(final int chunkX, final int chunkZ);
|
|
+
|
|
+ public ChunkData moonrise$requestChunkData(final long chunkKey);
|
|
+
|
|
+ public ChunkData moonrise$releaseChunkData(final long chunkKey);
|
|
+
|
|
+ public boolean moonrise$areChunksLoaded(final int fromX, final int fromZ, final int toX, final int toZ);
|
|
+
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java
|
|
index b8a87b7e6505feb76ce1bd58c84615256cf6faa6..9d46482476f9ed9032a2b0f89afc20e03ed42dbb 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemServerLevel.java
|
|
@@ -1,12 +1,13 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.level;
|
|
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
|
import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
|
import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.server.level.ChunkHolder;
|
|
import net.minecraft.server.level.ServerChunkCache;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
@@ -17,32 +18,34 @@ public interface ChunkSystemServerLevel extends ChunkSystemLevel {
|
|
|
|
public ChunkTaskScheduler moonrise$getChunkTaskScheduler();
|
|
|
|
- public RegionFileIOThread.ChunkDataController moonrise$getChunkDataController();
|
|
+ public MoonriseRegionFileIO.RegionDataController moonrise$getChunkDataController();
|
|
|
|
- public RegionFileIOThread.ChunkDataController moonrise$getPoiChunkDataController();
|
|
+ public MoonriseRegionFileIO.RegionDataController moonrise$getPoiChunkDataController();
|
|
|
|
- public RegionFileIOThread.ChunkDataController moonrise$getEntityChunkDataController();
|
|
+ public MoonriseRegionFileIO.RegionDataController moonrise$getEntityChunkDataController();
|
|
|
|
public int moonrise$getRegionChunkShift();
|
|
|
|
- // Paper - marked closing not needed on CB
|
|
+ public boolean moonrise$isMarkedClosing();
|
|
+
|
|
+ public void moonrise$setMarkedClosing(final boolean value);
|
|
|
|
public RegionizedPlayerChunkLoader moonrise$getPlayerChunkLoader();
|
|
|
|
public void moonrise$loadChunksAsync(final BlockPos pos, final int radiusBlocks,
|
|
- final PrioritisedExecutor.Priority priority,
|
|
+ final Priority priority,
|
|
final Consumer<List<ChunkAccess>> onLoad);
|
|
|
|
public void moonrise$loadChunksAsync(final BlockPos pos, final int radiusBlocks,
|
|
- final ChunkStatus chunkStatus, final PrioritisedExecutor.Priority priority,
|
|
+ final ChunkStatus chunkStatus, final Priority priority,
|
|
final Consumer<List<ChunkAccess>> onLoad);
|
|
|
|
public void moonrise$loadChunksAsync(final int minChunkX, final int maxChunkX, final int minChunkZ, final int maxChunkZ,
|
|
- final PrioritisedExecutor.Priority priority,
|
|
+ final Priority priority,
|
|
final Consumer<List<ChunkAccess>> onLoad);
|
|
|
|
public void moonrise$loadChunksAsync(final int minChunkX, final int maxChunkX, final int minChunkZ, final int maxChunkZ,
|
|
- final ChunkStatus chunkStatus, final PrioritisedExecutor.Priority priority,
|
|
+ final ChunkStatus chunkStatus, final Priority priority,
|
|
final Consumer<List<ChunkAccess>> onLoad);
|
|
|
|
public RegionizedPlayerChunkLoader.ViewDistanceHolder moonrise$getViewDistanceHolder();
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkData.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkData.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..8b9dc582627b46843f4b5ea6f8c3df2d8cac46fa
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkData.java
|
|
@@ -0,0 +1,21 @@
|
|
+package ca.spottedleaf.moonrise.patches.chunk_system.level.chunk;
|
|
+
|
|
+import ca.spottedleaf.moonrise.common.misc.NearbyPlayers;
|
|
+
|
|
+public final class ChunkData {
|
|
+
|
|
+ private int referenceCount = 0;
|
|
+ public NearbyPlayers.TrackedChunk nearbyPlayers; // Moonrise - nearby players
|
|
+
|
|
+ public ChunkData() {
|
|
+
|
|
+ }
|
|
+
|
|
+ public int increaseRef() {
|
|
+ return ++this.referenceCount;
|
|
+ }
|
|
+
|
|
+ public int decreaseRef() {
|
|
+ return --this.referenceCount;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemDistanceManager.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemDistanceManager.java
|
|
index 883fe6401f1b9711fa544d18a815b4d638f580df..aacd543f03b35908011d0c2891e978cc093ebcf5 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemDistanceManager.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkSystemDistanceManager.java
|
|
@@ -1,9 +1,12 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.level.chunk;
|
|
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
|
|
import net.minecraft.server.level.ChunkMap;
|
|
|
|
public interface ChunkSystemDistanceManager {
|
|
|
|
public ChunkMap moonrise$getChunkMap();
|
|
|
|
+ public ChunkHolderManager moonrise$getChunkHolderManager();
|
|
+
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
|
|
index 997b05167c19472acb98edac32d4548cc65efa8e..5ed6599d1f9a2edf8c904f3602b06d26d857600c 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java
|
|
@@ -1,6 +1,8 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.level.entity;
|
|
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import ca.spottedleaf.moonrise.common.list.EntityList;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
|
import com.google.common.collect.ImmutableList;
|
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
|
@@ -13,6 +15,7 @@ import net.minecraft.server.level.FullChunkStatus;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.entity.EntitySpawnReason;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.boss.EnderDragonPart;
|
|
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
|
@@ -26,7 +29,6 @@ import java.util.Arrays;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.function.Predicate;
|
|
-import org.bukkit.event.entity.EntityRemoveEvent;
|
|
|
|
public final class ChunkEntitySlices {
|
|
|
|
@@ -43,6 +45,7 @@ public final class ChunkEntitySlices {
|
|
private final EntityList entities = new EntityList();
|
|
|
|
public FullChunkStatus status;
|
|
+ public final ChunkData chunkData;
|
|
|
|
private boolean isTransient;
|
|
|
|
@@ -55,7 +58,7 @@ public final class ChunkEntitySlices {
|
|
}
|
|
|
|
public ChunkEntitySlices(final Level world, final int chunkX, final int chunkZ, final FullChunkStatus status,
|
|
- final int minSection, final int maxSection) { // inclusive, inclusive
|
|
+ final ChunkData chunkData, final int minSection, final int maxSection) { // inclusive, inclusive
|
|
this.minSection = minSection;
|
|
this.maxSection = maxSection;
|
|
this.chunkX = chunkX;
|
|
@@ -68,11 +71,12 @@ public final class ChunkEntitySlices {
|
|
this.entitiesByType = new Reference2ObjectOpenHashMap<>();
|
|
|
|
this.status = status;
|
|
+ this.chunkData = chunkData;
|
|
}
|
|
|
|
public static List<Entity> readEntities(final ServerLevel world, final CompoundTag compoundTag) {
|
|
// TODO check this and below on update for format changes
|
|
- return EntityType.loadEntitiesRecursive(compoundTag.getList("Entities", 10), world).collect(ImmutableList.toImmutableList());
|
|
+ return EntityType.loadEntitiesRecursive(compoundTag.getList("Entities", 10), world, EntitySpawnReason.LOAD).collect(ImmutableList.toImmutableList());
|
|
}
|
|
|
|
// Paper start - rewrite chunk system
|
|
@@ -100,7 +104,7 @@ public final class ChunkEntitySlices {
|
|
}
|
|
|
|
final ListTag entitiesTag = new ListTag();
|
|
- for (final Entity entity : entities) {
|
|
+ for (final Entity entity : PlatformHooks.get().modifySavedEntities(world, chunkPos.x, chunkPos.z, entities)) {
|
|
CompoundTag compoundTag = new CompoundTag();
|
|
if (entity.save(compoundTag)) {
|
|
entitiesTag.add(compoundTag);
|
|
@@ -147,12 +151,12 @@ public final class ChunkEntitySlices {
|
|
continue;
|
|
}
|
|
if (entity.shouldBeSaved()) {
|
|
- entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK, EntityRemoveEvent.Cause.UNLOAD);
|
|
+ PlatformHooks.get().unloadEntity(entity);
|
|
if (entity.isVehicle()) {
|
|
// we cannot assume that these entities are contained within this chunk, because entities can
|
|
// desync - so we need to remove them all
|
|
for (final Entity passenger : entity.getIndirectPassengers()) {
|
|
- passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK, EntityRemoveEvent.Cause.UNLOAD);
|
|
+ PlatformHooks.get().unloadEntity(passenger);
|
|
}
|
|
}
|
|
}
|
|
@@ -161,34 +165,7 @@ public final class ChunkEntitySlices {
|
|
return this.entities.size() != 0;
|
|
}
|
|
|
|
- // Paper start
|
|
- public org.bukkit.entity.Entity[] getChunkEntities() {
|
|
- List<org.bukkit.entity.Entity> ret = new java.util.ArrayList<>();
|
|
- final Entity[] entities = this.entities.getRawData();
|
|
- for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
|
|
- final Entity entity = entities[i];
|
|
- if (entity == null) {
|
|
- continue;
|
|
- }
|
|
- final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity();
|
|
- if (bukkit != null && bukkit.isValid()) {
|
|
- ret.add(bukkit);
|
|
- }
|
|
- }
|
|
-
|
|
- return ret.toArray(new org.bukkit.entity.Entity[0]);
|
|
- }
|
|
-
|
|
- public void callEntitiesLoadEvent() {
|
|
- org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
|
|
- }
|
|
-
|
|
- public void callEntitiesUnloadEvent() {
|
|
- org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
|
|
- }
|
|
- // Paper end
|
|
-
|
|
- private List<Entity> getAllEntities() {
|
|
+ public List<Entity> getAllEntities() {
|
|
final int len = this.entities.size();
|
|
if (len == 0) {
|
|
return new ArrayList<>();
|
|
@@ -251,6 +228,7 @@ public final class ChunkEntitySlices {
|
|
return false;
|
|
}
|
|
((ChunkSystemEntity)entity).moonrise$setChunkStatus(this.status);
|
|
+ ((ChunkSystemEntity)entity).moonrise$setChunkData(this.chunkData);
|
|
final int sectionIndex = chunkSection - this.minSection;
|
|
|
|
this.allEntities.addEntity(entity, sectionIndex);
|
|
@@ -284,6 +262,7 @@ public final class ChunkEntitySlices {
|
|
return false;
|
|
}
|
|
((ChunkSystemEntity)entity).moonrise$setChunkStatus(null);
|
|
+ ((ChunkSystemEntity)entity).moonrise$setChunkData(null);
|
|
final int sectionIndex = chunkSection - this.minSection;
|
|
|
|
this.allEntities.removeEntity(entity, sectionIndex);
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java
|
|
index efc0c1acc8239dd7b00211a1d3bfd3fc3b2c810c..93335de8cf514dc8417e4b9b2d495663deda2904 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/EntityLookup.java
|
|
@@ -46,8 +46,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
|
|
protected final SWMRLong2ObjectHashTable<ChunkSlicesRegion> regions = new SWMRLong2ObjectHashTable<>(128, 0.5f);
|
|
|
|
- protected final int minSection; // inclusive
|
|
- protected final int maxSection; // inclusive
|
|
protected final LevelCallback<Entity> worldCallback;
|
|
|
|
protected final ConcurrentLong2ReferenceChainedHashTable<Entity> entityById = new ConcurrentLong2ReferenceChainedHashTable<>();
|
|
@@ -56,8 +54,6 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
|
|
public EntityLookup(final Level world, final LevelCallback<Entity> worldCallback) {
|
|
this.world = world;
|
|
- this.minSection = WorldUtil.getMinSection(world);
|
|
- this.maxSection = WorldUtil.getMaxSection(world);
|
|
this.worldCallback = worldCallback;
|
|
}
|
|
|
|
@@ -91,7 +87,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
|
|
protected abstract void entityEndTicking(final Entity entity);
|
|
|
|
- protected abstract boolean screenEntity(final Entity entity);
|
|
+ protected abstract boolean screenEntity(final Entity entity, final boolean fromDisk, final boolean event);
|
|
|
|
private static Entity maskNonAccessible(final Entity entity) {
|
|
if (entity == null) {
|
|
@@ -347,7 +343,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
}
|
|
|
|
protected void addRecursivelySafe(final Entity root, final boolean fromDisk) {
|
|
- if (!this.addEntity(root, fromDisk)) {
|
|
+ if (!this.addEntity(root, fromDisk, true)) {
|
|
// possible we are a passenger, and so should dismount from any valid entity in the world
|
|
root.stopRiding();
|
|
return;
|
|
@@ -386,7 +382,11 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
}
|
|
|
|
public boolean addNewEntity(final Entity entity) {
|
|
- return this.addEntity(entity, false);
|
|
+ return this.addNewEntity(entity, true);
|
|
+ }
|
|
+
|
|
+ public boolean addNewEntity(final Entity entity, final boolean event) {
|
|
+ return this.addEntity(entity, false, event);
|
|
}
|
|
|
|
public static Visibility getEntityStatus(final Entity entity) {
|
|
@@ -397,10 +397,10 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
return Visibility.fromFullChunkStatus(entityStatus == null ? FullChunkStatus.INACCESSIBLE : entityStatus);
|
|
}
|
|
|
|
- protected boolean addEntity(final Entity entity, final boolean fromDisk) {
|
|
+ protected boolean addEntity(final Entity entity, final boolean fromDisk, final boolean event) {
|
|
final BlockPos pos = entity.blockPosition();
|
|
final int sectionX = pos.getX() >> 4;
|
|
- final int sectionY = Mth.clamp(pos.getY() >> 4, this.minSection, this.maxSection);
|
|
+ final int sectionY = Mth.clamp(pos.getY() >> 4, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world));
|
|
final int sectionZ = pos.getZ() >> 4;
|
|
this.checkThread(sectionX, sectionZ, "Cannot add entity off-main thread");
|
|
|
|
@@ -414,7 +414,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
return false;
|
|
}
|
|
|
|
- if (!this.screenEntity(entity)) {
|
|
+ if (!this.screenEntity(entity, fromDisk, event)) {
|
|
return false;
|
|
}
|
|
|
|
@@ -519,7 +519,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
final int sectionZ = ((ChunkSystemEntity)entity).moonrise$getSectionZ();
|
|
final BlockPos newPos = entity.blockPosition();
|
|
final int newSectionX = newPos.getX() >> 4;
|
|
- final int newSectionY = Mth.clamp(newPos.getY() >> 4, this.minSection, this.maxSection);
|
|
+ final int newSectionY = Mth.clamp(newPos.getY() >> 4, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world));
|
|
final int newSectionZ = newPos.getZ() >> 4;
|
|
|
|
if (newSectionX == sectionX && newSectionY == sectionY && newSectionZ == sectionZ) {
|
|
@@ -959,7 +959,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
|
|
public ChunkEntitySlices getOrCreateChunk(final int chunkX, final int chunkZ) {
|
|
final ChunkSlicesRegion region = this.getRegion(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
|
|
- ChunkEntitySlices ret;
|
|
+ final ChunkEntitySlices ret;
|
|
if (region == null || (ret = region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT))) == null) {
|
|
return this.createEntityChunk(chunkX, chunkZ, true);
|
|
}
|
|
@@ -1057,7 +1057,7 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
@Override
|
|
public void onRemove(final Entity.RemovalReason reason) {
|
|
final Entity entity = this.entity;
|
|
- EntityLookup.this.checkThread(entity, "Cannot remove entity off-main"); // Paper - rewrite chunk system
|
|
+ EntityLookup.this.checkThread(entity, "Cannot remove entity off-main");
|
|
final Visibility tickingState = EntityLookup.getEntityStatus(entity);
|
|
|
|
EntityLookup.this.removeEntity(entity);
|
|
@@ -1080,4 +1080,4 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
|
|
@Override
|
|
public void onRemove(final Entity.RemovalReason reason) {}
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/client/ClientEntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/client/ClientEntityLookup.java
|
|
index edcde00206d068bd79175fea33efa05b0e8c1562..a038215156a163b0b1cbc870ada5b4ac85ed1335 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/client/ClientEntityLookup.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/client/ClientEntityLookup.java
|
|
@@ -1,5 +1,6 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.level.entity.client;
|
|
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
|
|
@@ -45,7 +46,8 @@ public final class ClientEntityLookup extends EntityLookup {
|
|
|
|
final ChunkEntitySlices ret = new ChunkEntitySlices(
|
|
this.world, chunkX, chunkZ,
|
|
- ticking ? FullChunkStatus.ENTITY_TICKING : FullChunkStatus.FULL, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
|
+ ticking ? FullChunkStatus.ENTITY_TICKING : FullChunkStatus.FULL, null,
|
|
+ WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
|
);
|
|
|
|
// note: not handled by superclass
|
|
@@ -63,7 +65,11 @@ public final class ClientEntityLookup extends EntityLookup {
|
|
protected void entitySectionChangeCallback(final Entity entity,
|
|
final int oldSectionX, final int oldSectionY, final int oldSectionZ,
|
|
final int newSectionX, final int newSectionY, final int newSectionZ) {
|
|
-
|
|
+ PlatformHooks.get().entityMove(
|
|
+ entity,
|
|
+ CoordinateUtils.getChunkSectionKey(oldSectionX, oldSectionY, oldSectionZ),
|
|
+ CoordinateUtils.getChunkSectionKey(newSectionX, newSectionY, newSectionZ)
|
|
+ );
|
|
}
|
|
|
|
@Override
|
|
@@ -97,7 +103,7 @@ public final class ClientEntityLookup extends EntityLookup {
|
|
}
|
|
|
|
@Override
|
|
- protected boolean screenEntity(final Entity entity) {
|
|
+ protected boolean screenEntity(final Entity entity, final boolean fromDisk, final boolean event) {
|
|
return true;
|
|
}
|
|
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/dfl/DefaultEntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/dfl/DefaultEntityLookup.java
|
|
index 465469e44346c50f30f3abd6b44f4173ccfcf248..2ff58cf753c60913ee73aae015182e9c5560d529 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/dfl/DefaultEntityLookup.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/dfl/DefaultEntityLookup.java
|
|
@@ -32,7 +32,7 @@ public final class DefaultEntityLookup extends EntityLookup {
|
|
protected ChunkEntitySlices createEntityChunk(final int chunkX, final int chunkZ, final boolean transientChunk) {
|
|
final ChunkEntitySlices ret = new ChunkEntitySlices(
|
|
this.world, chunkX, chunkZ, FullChunkStatus.FULL,
|
|
- WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
|
+ null, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
|
);
|
|
|
|
// note: not handled by superclass
|
|
@@ -84,7 +84,7 @@ public final class DefaultEntityLookup extends EntityLookup {
|
|
}
|
|
|
|
@Override
|
|
- protected boolean screenEntity(final Entity entity) {
|
|
+ protected boolean screenEntity(final Entity entity, final boolean fromDisk, final boolean event) {
|
|
return true;
|
|
}
|
|
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
|
|
index dacf2b2988ce603879fe525a3418ac77f8a663f7..58d9187adc188b693b6becc400f766e069bf1bf5 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/server/ServerEntityLookup.java
|
|
@@ -1,6 +1,8 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server;
|
|
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
|
+import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
|
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
|
@@ -17,7 +19,6 @@ public final class ServerEntityLookup extends EntityLookup {
|
|
|
|
private final ServerLevel serverWorld;
|
|
public final ReferenceList<Entity> trackerEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker
|
|
- public final ReferenceList<Entity> trackerUnloadedEntities = new ReferenceList<>(EMPTY_ENTITY_ARRAY); // Moonrise - entity tracker
|
|
|
|
public ServerEntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
|
|
super(world, worldCallback);
|
|
@@ -63,6 +64,11 @@ public final class ServerEntityLookup extends EntityLookup {
|
|
if (entity instanceof ServerPlayer player) {
|
|
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().tickPlayer(player);
|
|
}
|
|
+ PlatformHooks.get().entityMove(
|
|
+ entity,
|
|
+ CoordinateUtils.getChunkSectionKey(oldSectionX, oldSectionY, oldSectionZ),
|
|
+ CoordinateUtils.getChunkSectionKey(newSectionX, newSectionY, newSectionZ)
|
|
+ );
|
|
}
|
|
|
|
@Override
|
|
@@ -77,14 +83,12 @@ public final class ServerEntityLookup extends EntityLookup {
|
|
if (entity instanceof ServerPlayer player) {
|
|
((ChunkSystemServerLevel)this.serverWorld).moonrise$getNearbyPlayers().removePlayer(player);
|
|
}
|
|
- this.trackerUnloadedEntities.remove(entity); // Moonrise - entity tracker
|
|
}
|
|
|
|
@Override
|
|
protected void entityStartLoaded(final Entity entity) {
|
|
// Moonrise start - entity tracker
|
|
this.trackerEntities.add(entity);
|
|
- this.trackerUnloadedEntities.remove(entity);
|
|
// Moonrise end - entity tracker
|
|
}
|
|
|
|
@@ -92,7 +96,6 @@ public final class ServerEntityLookup extends EntityLookup {
|
|
protected void entityEndLoaded(final Entity entity) {
|
|
// Moonrise start - entity tracker
|
|
this.trackerEntities.remove(entity);
|
|
- this.trackerUnloadedEntities.add(entity);
|
|
// Moonrise end - entity tracker
|
|
}
|
|
|
|
@@ -107,7 +110,7 @@ public final class ServerEntityLookup extends EntityLookup {
|
|
}
|
|
|
|
@Override
|
|
- protected boolean screenEntity(final Entity entity) {
|
|
- return ChunkSystem.screenEntity(this.serverWorld, entity);
|
|
+ protected boolean screenEntity(final Entity entity, final boolean fromDisk, final boolean event) {
|
|
+ return ChunkSystem.screenEntity(this.serverWorld, entity, fromDisk, event);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/poi/PoiChunk.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/poi/PoiChunk.java
|
|
index fd35e4db0c8fec8f86b8743bcc2b15ed2e7433f1..bbf9d6c1c9525d97160806819a57be03eca290f1 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/poi/PoiChunk.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/poi/PoiChunk.java
|
|
@@ -3,7 +3,6 @@ package ca.spottedleaf.moonrise.patches.chunk_system.level.poi;
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
-import com.mojang.serialization.Codec;
|
|
import com.mojang.serialization.DataResult;
|
|
import net.minecraft.SharedConstants;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
@@ -123,7 +122,6 @@ public final class PoiChunk {
|
|
ret.putInt("DataVersion", SharedConstants.getCurrentVersion().getDataVersion().getVersion());
|
|
|
|
final ServerLevel world = this.world;
|
|
- final PoiManager poiManager = world.getPoiManager();
|
|
final int chunkX = this.chunkX;
|
|
final int chunkZ = this.chunkZ;
|
|
|
|
@@ -133,13 +131,8 @@ public final class PoiChunk {
|
|
continue;
|
|
}
|
|
|
|
- final long key = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
|
|
- // codecs are honestly such a fucking disaster. What the fuck is this trash?
|
|
- final Codec<PoiSection> codec = PoiSection.codec(() -> {
|
|
- poiManager.setDirty(key);
|
|
- });
|
|
-
|
|
- final DataResult<Tag> serializedResult = codec.encodeStart(registryOps, section);
|
|
+ // I do not believe asynchronously converting to CompoundTag is worth the scheduling.
|
|
+ final DataResult<Tag> serializedResult = PoiSection.Packed.CODEC.encodeStart(registryOps, section.pack());
|
|
final int finalSectionY = sectionY;
|
|
final Tag serialized = serializedResult.resultOrPartial((final String description) -> {
|
|
LOGGER.error("Failed to serialize poi chunk for world: " + WorldUtil.getWorldName(world) + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
|
|
@@ -183,19 +176,18 @@ public final class PoiChunk {
|
|
continue;
|
|
}
|
|
|
|
- final long coordinateKey = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
|
|
- // codecs are honestly such a fucking disaster. What the fuck is this trash?
|
|
- final Codec<PoiSection> codec = PoiSection.codec(() -> {
|
|
- poiManager.setDirty(coordinateKey);
|
|
- });
|
|
-
|
|
final CompoundTag section = sections.getCompound(key);
|
|
- final DataResult<PoiSection> deserializeResult = codec.parse(registryOps, section);
|
|
+ final DataResult<PoiSection.Packed> deserializeResult = PoiSection.Packed.CODEC.parse(registryOps, section);
|
|
final int finalSectionY = sectionY;
|
|
- final PoiSection deserialized = deserializeResult.resultOrPartial((final String description) -> {
|
|
+ final PoiSection.Packed packed = deserializeResult.resultOrPartial((final String description) -> {
|
|
LOGGER.error("Failed to deserialize poi chunk for world: " + WorldUtil.getWorldName(world) + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
|
|
}).orElse(null);
|
|
|
|
+ final long coordinateKey = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
|
|
+ final PoiSection deserialized = packed == null ? null : packed.unpack(() -> {
|
|
+ poiManager.setDirty(coordinateKey);
|
|
+ });
|
|
+
|
|
if (deserialized == null || ((ChunkSystemPoiSection)deserialized).moonrise$isEmpty()) {
|
|
// completely empty, no point in storing this
|
|
continue;
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/storage/ChunkSystemSectionStorage.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/storage/ChunkSystemSectionStorage.java
|
|
index fb87d7ece6ebccfd0ffd2f1a609b45a0d2461d9e..524752744e37a2db0e3ea089468bdf497129bfef 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/storage/ChunkSystemSectionStorage.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/storage/ChunkSystemSectionStorage.java
|
|
@@ -1,12 +1,8 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.level.storage;
|
|
|
|
-import com.mojang.serialization.Dynamic;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
-import net.minecraft.nbt.Tag;
|
|
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
|
import java.io.IOException;
|
|
-import java.util.Optional;
|
|
-import java.util.concurrent.CompletableFuture;
|
|
|
|
public interface ChunkSystemSectionStorage {
|
|
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
index 852d75a73dae7448cbe1e2f5e164b235efa8a969..b2fa9883aefb07f64bb5db7e0052218d2ad09aba 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
|
@@ -1,7 +1,8 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.player;
|
|
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import ca.spottedleaf.moonrise.common.misc.AllocatingRateLimiter;
|
|
import ca.spottedleaf.moonrise.common.misc.SingleUserAreaMap;
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
@@ -412,7 +413,11 @@ public final class RegionizedPlayerChunkLoader {
|
|
if (this.sentChunks.add(CoordinateUtils.getChunkKey(chunkX, chunkZ))) {
|
|
((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
|
.getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$addReceivedChunk(this.player);
|
|
- PlayerChunkSender.sendChunk(this.player.connection, this.world, ((ChunkSystemLevel)this.world).moonrise$getFullChunkIfLoaded(chunkX, chunkZ));
|
|
+
|
|
+ final LevelChunk chunk = ((ChunkSystemLevel)this.world).moonrise$getFullChunkIfLoaded(chunkX, chunkZ);
|
|
+
|
|
+ PlatformHooks.get().onChunkWatch(this.world, chunk, this.player);
|
|
+ PlayerChunkSender.sendChunk(this.player.connection, this.world, chunk);
|
|
return;
|
|
}
|
|
throw new IllegalStateException();
|
|
@@ -426,17 +431,12 @@ public final class RegionizedPlayerChunkLoader {
|
|
}
|
|
|
|
private void sendUnloadChunkRaw(final int chunkX, final int chunkZ) {
|
|
+ PlatformHooks.get().onChunkUnWatch(this.world, new ChunkPos(chunkX, chunkZ), this.player);
|
|
// Note: Check PlayerChunkSender#dropChunk for other logic
|
|
// Note: drop isAlive() check so that chunks properly unload client-side when the player dies
|
|
((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
|
.getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$removeReceivedChunk(this.player);
|
|
- final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
|
|
- this.player.connection.send(new ClientboundForgetLevelChunkPacket(chunkPos));
|
|
- // Paper start - PlayerChunkUnloadEvent
|
|
- if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
|
- new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(this.world.getWorld().getChunkAt(chunkPos.longKey), this.player.getBukkitEntity()).callEvent();
|
|
- }
|
|
- // Paper end - PlayerChunkUnloadEvent
|
|
+ this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)));
|
|
}
|
|
|
|
private final SingleUserAreaMap<PlayerChunkLoaderData> broadcastMap = new SingleUserAreaMap<>(this) {
|
|
@@ -529,7 +529,7 @@ public final class RegionizedPlayerChunkLoader {
|
|
final int playerSendViewDistance, final int worldSendViewDistance) {
|
|
return Math.min(
|
|
loadViewDistance - 1,
|
|
- playerSendViewDistance < 0 ? (!io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingAdvanced.autoConfigSendDistance || clientViewDistance < 0 ? (worldSendViewDistance < 0 ? (loadViewDistance - 1) : worldSendViewDistance) : clientViewDistance + 1) : playerSendViewDistance
|
|
+ playerSendViewDistance < 0 ? (!PlatformHooks.get().configAutoConfigSendDistance() || clientViewDistance < 0 ? (worldSendViewDistance < 0 ? (loadViewDistance - 1) : worldSendViewDistance) : clientViewDistance + 1) : playerSendViewDistance
|
|
);
|
|
}
|
|
|
|
@@ -554,26 +554,26 @@ public final class RegionizedPlayerChunkLoader {
|
|
}
|
|
|
|
private double getMaxChunkLoadRate() {
|
|
- final double configRate = io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingBasic.playerMaxChunkLoadRate;
|
|
+ final double configRate = PlatformHooks.get().configPlayerMaxLoadRate();
|
|
|
|
return configRate <= 0.0 || configRate > (double)MAX_RATE ? (double)MAX_RATE : Math.max(1.0, configRate);
|
|
}
|
|
|
|
private double getMaxChunkGenRate() {
|
|
- final double configRate = io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingBasic.playerMaxChunkGenerateRate;
|
|
+ final double configRate = PlatformHooks.get().configPlayerMaxGenRate();
|
|
|
|
return configRate <= 0.0 || configRate > (double)MAX_RATE ? (double)MAX_RATE : Math.max(1.0, configRate);
|
|
}
|
|
|
|
private double getMaxChunkSendRate() {
|
|
- final double configRate = io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingBasic.playerMaxChunkSendRate;
|
|
+ final double configRate = PlatformHooks.get().configPlayerMaxSendRate();
|
|
|
|
return configRate <= 0.0 || configRate > (double)MAX_RATE ? (double)MAX_RATE : Math.max(1.0, configRate);
|
|
}
|
|
|
|
private long getMaxChunkLoads() {
|
|
final long radiusChunks = (2L * this.lastLoadDistance + 1L) * (2L * this.lastLoadDistance + 1L);
|
|
- long configLimit = io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingAdvanced.playerMaxConcurrentChunkLoads;
|
|
+ long configLimit = (long)PlatformHooks.get().configPlayerMaxConcurrentLoads();
|
|
if (configLimit == 0L) {
|
|
// by default, only allow 1/5th of the chunks in the view distance to be concurrently active
|
|
configLimit = Math.max(5L, radiusChunks / 5L);
|
|
@@ -587,7 +587,7 @@ public final class RegionizedPlayerChunkLoader {
|
|
|
|
private long getMaxChunkGenerates() {
|
|
final long radiusChunks = (2L * this.lastLoadDistance + 1L) * (2L * this.lastLoadDistance + 1L);
|
|
- long configLimit = io.papermc.paper.configuration.GlobalConfiguration.get().chunkLoadingAdvanced.playerMaxConcurrentChunkGenerates;
|
|
+ long configLimit = (long)PlatformHooks.get().configPlayerMaxConcurrentGens();
|
|
if (configLimit == 0L) {
|
|
// by default, only allow 1/5th of the chunks in the view distance to be concurrently active
|
|
configLimit = Math.max(5L, radiusChunks / 5L);
|
|
@@ -709,7 +709,7 @@ public final class RegionizedPlayerChunkLoader {
|
|
final int queuedChunkX = CoordinateUtils.getChunkX(queuedLoadChunk);
|
|
final int queuedChunkZ = CoordinateUtils.getChunkZ(queuedLoadChunk);
|
|
((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().scheduleChunkLoad(
|
|
- queuedChunkX, queuedChunkZ, ChunkStatus.EMPTY, false, PrioritisedExecutor.Priority.NORMAL, null
|
|
+ queuedChunkX, queuedChunkZ, ChunkStatus.EMPTY, false, Priority.NORMAL, null
|
|
);
|
|
if (this.removed) {
|
|
return;
|
|
@@ -825,7 +825,7 @@ public final class RegionizedPlayerChunkLoader {
|
|
}
|
|
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
|
|
// not yet post-processed, need to do this so that tile entities can properly be sent to clients
|
|
- chunk.postProcessGeneration();
|
|
+ chunk.postProcessGeneration(this.world);
|
|
// check if there was any recursive action
|
|
if (this.removed || this.sendQueue.isEmpty() || this.sendQueue.firstLong() != pendingSend) {
|
|
return;
|
|
@@ -864,7 +864,6 @@ public final class RegionizedPlayerChunkLoader {
|
|
final int clientViewDistance = getClientViewDistance(this.player);
|
|
final int sendViewDistance = getSendViewDistance(loadViewDistance, clientViewDistance, playerDistances.sendViewDistance, worldDistances.sendViewDistance);
|
|
|
|
- // TODO check PlayerList diff in paper chunk system patch
|
|
// send view distances
|
|
this.player.connection.send(this.updateClientChunkRadius(sendViewDistance));
|
|
this.player.connection.send(this.updateClientSimulationDistance(tickViewDistance));
|
|
@@ -1078,5 +1077,9 @@ public final class RegionizedPlayerChunkLoader {
|
|
|
|
// now all tickets should be removed, which is all of our external state
|
|
}
|
|
+
|
|
+ public LongOpenHashSet getSentChunksRaw() {
|
|
+ return this.sentChunks;
|
|
+ }
|
|
}
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
|
index 58d3d1a47e9f2423c467bb329c2d5f4b58a8b5ef..f98df65eaed2abedc66f3a49790e0cfb65354ed9 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkHolderManager.java
|
|
@@ -1,14 +1,14 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
|
|
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
|
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
-import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
|
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk;
|
|
@@ -20,7 +20,6 @@ import ca.spottedleaf.moonrise.patches.chunk_system.ticket.ChunkSystemTicket;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.util.ChunkSystemSortedArraySet;
|
|
import com.google.gson.JsonArray;
|
|
import com.google.gson.JsonObject;
|
|
-import com.mojang.logging.LogUtils;
|
|
import it.unimi.dsi.fastutil.longs.Long2ByteLinkedOpenHashMap;
|
|
import it.unimi.dsi.fastutil.longs.Long2ByteMap;
|
|
import it.unimi.dsi.fastutil.longs.Long2IntMap;
|
|
@@ -40,7 +39,9 @@ import net.minecraft.server.level.TicketType;
|
|
import net.minecraft.util.SortedArraySet;
|
|
import net.minecraft.util.Unit;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
+import net.minecraft.world.level.chunk.LevelChunk;
|
|
import org.slf4j.Logger;
|
|
+import org.slf4j.LoggerFactory;
|
|
import java.io.IOException;
|
|
import java.text.DecimalFormat;
|
|
import java.util.ArrayDeque;
|
|
@@ -58,7 +59,7 @@ import java.util.function.Predicate;
|
|
|
|
public final class ChunkHolderManager {
|
|
|
|
- private static final Logger LOGGER = LogUtils.getClassLogger();
|
|
+ private static final Logger LOGGER = LoggerFactory.getLogger(ChunkHolderManager.class);
|
|
|
|
public static final int FULL_LOADED_TICKET_LEVEL = ChunkLevel.FULL_CHUNK_LEVEL;
|
|
public static final int BLOCK_TICKING_TICKET_LEVEL = ChunkLevel.BLOCK_TICKING_LEVEL;
|
|
@@ -189,7 +190,7 @@ public final class ChunkHolderManager {
|
|
if (halt) {
|
|
LOGGER.info("Waiting 60s for chunk system to halt for world '" + WorldUtil.getWorldName(this.world) + "'");
|
|
if (!this.taskScheduler.halt(true, TimeUnit.SECONDS.toNanos(60L))) {
|
|
- LOGGER.warn("Failed to halt world generation/loading tasks for world '" + WorldUtil.getWorldName(this.world) + "'");
|
|
+ LOGGER.warn("Failed to halt generation/loading tasks for world '" + WorldUtil.getWorldName(this.world) + "'");
|
|
} else {
|
|
LOGGER.info("Halted chunk system for world '" + WorldUtil.getWorldName(this.world) + "'");
|
|
}
|
|
@@ -199,21 +200,21 @@ public final class ChunkHolderManager {
|
|
this.saveAllChunks(true, true, true);
|
|
}
|
|
|
|
- boolean hasTasks = false;
|
|
- for (final RegionFileIOThread.RegionFileType type : RegionFileIOThread.RegionFileType.values()) {
|
|
- if (RegionFileIOThread.getControllerFor(this.world, type).hasTasks()) {
|
|
- hasTasks = true;
|
|
- break;
|
|
+ MoonriseRegionFileIO.flush(this.world);
|
|
+
|
|
+ if (halt) {
|
|
+ LOGGER.info("Waiting 60s for chunk I/O to halt for world '" + WorldUtil.getWorldName(this.world) + "'");
|
|
+ if (!this.taskScheduler.haltIO(true, TimeUnit.SECONDS.toNanos(60L))) {
|
|
+ LOGGER.warn("Failed to halt I/O tasks for world '" + WorldUtil.getWorldName(this.world) + "'");
|
|
+ } else {
|
|
+ LOGGER.info("Halted I/O scheduler for world '" + WorldUtil.getWorldName(this.world) + "'");
|
|
}
|
|
}
|
|
- if (hasTasks) {
|
|
- RegionFileIOThread.flush();
|
|
- }
|
|
|
|
// kill regionfile cache
|
|
- for (final RegionFileIOThread.RegionFileType type : RegionFileIOThread.RegionFileType.values()) {
|
|
+ for (final MoonriseRegionFileIO.RegionFileType type : MoonriseRegionFileIO.RegionFileType.values()) {
|
|
try {
|
|
- RegionFileIOThread.getControllerFor(this.world, type).getCache().close();
|
|
+ MoonriseRegionFileIO.getControllerFor(this.world, type).getCache().close();
|
|
} catch (final IOException ex) {
|
|
LOGGER.error("Failed to close '" + type.name() + "' regionfile cache for world '" + WorldUtil.getWorldName(this.world) + "'", ex);
|
|
}
|
|
@@ -230,8 +231,8 @@ public final class ChunkHolderManager {
|
|
public void autoSave() {
|
|
final List<NewChunkHolder> reschedule = new ArrayList<>();
|
|
final long currentTick = this.currentTick;
|
|
- final long maxSaveTime = currentTick - Math.max(1L, this.world.paperConfig().chunks.autoSaveInterval.value());
|
|
- final int maxToSave = this.world.paperConfig().chunks.maxAutoSaveChunksPerTick;
|
|
+ final long maxSaveTime = currentTick - Math.max(1L, PlatformHooks.get().configAutoSaveInterval());
|
|
+ final int maxToSave = PlatformHooks.get().configMaxAutoSavePerTick();
|
|
for (int autoSaved = 0; autoSaved < maxToSave && !this.autoSaveQueue.isEmpty();) {
|
|
final NewChunkHolder holder = this.autoSaveQueue.first();
|
|
|
|
@@ -271,55 +272,74 @@ public final class ChunkHolderManager {
|
|
|
|
long start = System.nanoTime();
|
|
long lastLog = start;
|
|
- boolean needsFlush = false;
|
|
- final int flushInterval = 50;
|
|
+ final int flushInterval = 200;
|
|
+ int lastFlush = 0;
|
|
|
|
int savedChunk = 0;
|
|
int savedEntity = 0;
|
|
int savedPoi = 0;
|
|
|
|
+ if (shutdown) {
|
|
+ // Normal unload process does not occur during shutdown: fire event manually
|
|
+ // for mods that expect ChunkEvent.Unload to fire on shutdown (before LevelEvent.Unload)
|
|
+ for (int i = 0, len = holders.size(); i < len; ++i) {
|
|
+ final NewChunkHolder holder = holders.get(i);
|
|
+ if (holder.getCurrentChunk() instanceof LevelChunk levelChunk) {
|
|
+ PlatformHooks.get().chunkUnloadFromWorld(levelChunk);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
for (int i = 0, len = holders.size(); i < len; ++i) {
|
|
final NewChunkHolder holder = holders.get(i);
|
|
try {
|
|
final NewChunkHolder.SaveStat saveStat = holder.save(shutdown);
|
|
if (saveStat != null) {
|
|
- ++saved;
|
|
- needsFlush = flush;
|
|
if (saveStat.savedChunk()) {
|
|
++savedChunk;
|
|
+ ++saved;
|
|
}
|
|
if (saveStat.savedEntityChunk()) {
|
|
++savedEntity;
|
|
+ ++saved;
|
|
}
|
|
if (saveStat.savedPoiChunk()) {
|
|
++savedPoi;
|
|
+ ++saved;
|
|
}
|
|
}
|
|
} catch (final Throwable thr) {
|
|
LOGGER.error("Failed to save chunk (" + holder.chunkX + "," + holder.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "'", thr);
|
|
}
|
|
- if (needsFlush && (saved % flushInterval) == 0) {
|
|
- needsFlush = false;
|
|
- RegionFileIOThread.partialFlush(flushInterval / 2);
|
|
+ if (flush && (saved - lastFlush) > (flushInterval / 2)) {
|
|
+ lastFlush = saved;
|
|
+ MoonriseRegionFileIO.partialFlush(this.world, flushInterval / 2);
|
|
}
|
|
if (logProgress) {
|
|
final long currTime = System.nanoTime();
|
|
if ((currTime - lastLog) > TimeUnit.SECONDS.toNanos(10L)) {
|
|
lastLog = currTime;
|
|
- LOGGER.info("Saved " + saved + " chunks (" + format.format((double)(i+1)/(double)len * 100.0) + "%) in world '" + WorldUtil.getWorldName(this.world) + "'");
|
|
+ LOGGER.info(
|
|
+ "Saved " + savedChunk + " block chunks, " + savedEntity + " entity chunks, " + savedPoi
|
|
+ + " poi chunks in world '" + WorldUtil.getWorldName(this.world) + "', progress: "
|
|
+ + format.format((double)(i+1)/(double)len * 100.0)
|
|
+ );
|
|
}
|
|
}
|
|
}
|
|
if (flush) {
|
|
- RegionFileIOThread.flush();
|
|
+ MoonriseRegionFileIO.flush(this.world);
|
|
try {
|
|
- RegionFileIOThread.flushRegionStorages(this.world);
|
|
+ MoonriseRegionFileIO.flushRegionStorages(this.world);
|
|
} catch (final IOException ex) {
|
|
LOGGER.error("Exception when flushing regions in world '" + WorldUtil.getWorldName(this.world) + "'", ex);
|
|
}
|
|
}
|
|
if (logProgress) {
|
|
- LOGGER.info("Saved " + savedChunk + " block chunks, " + savedEntity + " entity chunks, " + savedPoi + " poi chunks in world '" + WorldUtil.getWorldName(this.world) + "' in " + format.format(1.0E-9 * (System.nanoTime() - start)) + "s");
|
|
+ LOGGER.info(
|
|
+ "Saved " + savedChunk + " block chunks, " + savedEntity + " entity chunks, " + savedPoi
|
|
+ + " poi chunks in world '" + WorldUtil.getWorldName(this.world) + "' in "
|
|
+ + format.format(1.0E-9 * (System.nanoTime() - start)) + "s"
|
|
+ );
|
|
}
|
|
}
|
|
|
|
@@ -798,21 +818,21 @@ public final class ChunkHolderManager {
|
|
return this.chunkHolders.get(position);
|
|
}
|
|
|
|
- public void raisePriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
|
|
+ public void raisePriority(final int x, final int z, final Priority priority) {
|
|
final NewChunkHolder chunkHolder = this.getChunkHolder(x, z);
|
|
if (chunkHolder != null) {
|
|
chunkHolder.raisePriority(priority);
|
|
}
|
|
}
|
|
|
|
- public void setPriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
|
|
+ public void setPriority(final int x, final int z, final Priority priority) {
|
|
final NewChunkHolder chunkHolder = this.getChunkHolder(x, z);
|
|
if (chunkHolder != null) {
|
|
chunkHolder.setPriority(priority);
|
|
}
|
|
}
|
|
|
|
- public void lowerPriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
|
|
+ public void lowerPriority(final int x, final int z, final Priority priority) {
|
|
final NewChunkHolder chunkHolder = this.getChunkHolder(x, z);
|
|
if (chunkHolder != null) {
|
|
chunkHolder.lowerPriority(priority);
|
|
@@ -895,7 +915,7 @@ public final class ChunkHolderManager {
|
|
final ChunkLoadTask.EntityDataLoadTask entityLoad = current.getEntityDataLoadTask();
|
|
|
|
if (entityLoad != null) {
|
|
- entityLoad.raisePriority(PrioritisedExecutor.Priority.BLOCKING);
|
|
+ entityLoad.raisePriority(Priority.BLOCKING);
|
|
}
|
|
}
|
|
}
|
|
@@ -971,7 +991,7 @@ public final class ChunkHolderManager {
|
|
final ChunkLoadTask.PoiDataLoadTask poiLoad = current.getPoiDataLoadTask();
|
|
|
|
if (poiLoad != null) {
|
|
- poiLoad.raisePriority(PrioritisedExecutor.Priority.BLOCKING);
|
|
+ poiLoad.raisePriority(Priority.BLOCKING);
|
|
}
|
|
}
|
|
} finally {
|
|
@@ -1018,7 +1038,7 @@ public final class ChunkHolderManager {
|
|
}
|
|
|
|
ChunkHolderManager.this.processPendingFullUpdate();
|
|
- }, PrioritisedExecutor.Priority.HIGHEST);
|
|
+ }, Priority.HIGHEST);
|
|
} else {
|
|
final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
|
|
for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
|
|
@@ -1028,11 +1048,10 @@ public final class ChunkHolderManager {
|
|
}
|
|
|
|
private void removeChunkHolder(final NewChunkHolder holder) {
|
|
- holder.markUnloaded();
|
|
+ holder.onUnload();
|
|
this.autoSaveQueue.remove(holder);
|
|
ChunkSystem.onChunkHolderDelete(this.world, holder.vanillaChunkHolder);
|
|
this.chunkHolders.remove(CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ));
|
|
-
|
|
}
|
|
|
|
// note: never call while inside the chunk system, this will absolutely break everything
|
|
@@ -1313,6 +1332,9 @@ public final class ChunkHolderManager {
|
|
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
|
|
throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager");
|
|
}
|
|
+ if (!PlatformHooks.get().allowAsyncTicketUpdates() && !TickThread.isTickThread()) {
|
|
+ TickThread.ensureTickThread("Cannot asynchronously process ticket updates");
|
|
+ }
|
|
|
|
List<NewChunkHolder> changedFullStatus = null;
|
|
|
|
@@ -1328,10 +1350,15 @@ public final class ChunkHolderManager {
|
|
}
|
|
changedFullStatus = new ArrayList<>();
|
|
|
|
- ret |= this.ticketLevelPropagator.performUpdates(
|
|
- this.ticketLockArea, this.taskScheduler.schedulingLockArea,
|
|
- scheduledTasks, changedFullStatus
|
|
- );
|
|
+ this.blockTicketUpdates();
|
|
+ try {
|
|
+ ret |= this.ticketLevelPropagator.performUpdates(
|
|
+ this.ticketLockArea, this.taskScheduler.schedulingLockArea,
|
|
+ scheduledTasks, changedFullStatus
|
|
+ );
|
|
+ } finally {
|
|
+ this.unblockTicketUpdates(Boolean.FALSE);
|
|
+ }
|
|
}
|
|
|
|
if (changedFullStatus != null) {
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java
|
|
index 8671a90e969d16c7a57ddc38fedb7cf01815f64c..120ce31729dc8d4bba0901ca06d3212f3158d089 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/ChunkTaskScheduler.java
|
|
@@ -1,16 +1,17 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
|
|
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadPool;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadedTaskQueue;
|
|
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.executor.queue.PrioritisedTaskQueue;
|
|
+import ca.spottedleaf.concurrentutil.executor.thread.PrioritisedThreadPool;
|
|
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
+import ca.spottedleaf.moonrise.common.config.moonrise.MoonriseConfig;
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
import ca.spottedleaf.moonrise.common.util.JsonUtil;
|
|
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
|
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkStatus;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer;
|
|
@@ -23,7 +24,6 @@ import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task.ChunkUpgrade
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.status.ChunkSystemChunkStep;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
|
|
-import com.mojang.logging.LogUtils;
|
|
import com.google.gson.JsonArray;
|
|
import com.google.gson.JsonObject;
|
|
import net.minecraft.CrashReport;
|
|
@@ -48,6 +48,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
import net.minecraft.world.level.chunk.status.ChunkStep;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.slf4j.Logger;
|
|
+import org.slf4j.LoggerFactory;
|
|
import java.io.File;
|
|
import java.time.LocalDateTime;
|
|
import java.time.format.DateTimeFormatter;
|
|
@@ -63,51 +64,14 @@ import java.util.function.Consumer;
|
|
|
|
public final class ChunkTaskScheduler {
|
|
|
|
- private static final Logger LOGGER = LogUtils.getClassLogger();
|
|
+ private static final Logger LOGGER = LoggerFactory.getLogger(ChunkTaskScheduler.class);
|
|
|
|
- static int newChunkSystemIOThreads;
|
|
- static int newChunkSystemGenParallelism;
|
|
- static int newChunkSystemGenPopulationParallelism;
|
|
- static int newChunkSystemLoadParallelism;
|
|
-
|
|
- private static boolean initialised = false;
|
|
-
|
|
- public static void init(io.papermc.paper.configuration.GlobalConfiguration.ChunkSystem chunkSystem) {
|
|
- if (initialised) {
|
|
- return;
|
|
- }
|
|
- initialised = true;
|
|
- MoonriseCommon.init(chunkSystem); // Paper
|
|
- newChunkSystemIOThreads = chunkSystem.ioThreads;
|
|
- if (newChunkSystemIOThreads <= 0) {
|
|
- newChunkSystemIOThreads = 1;
|
|
- } else {
|
|
- newChunkSystemIOThreads = Math.max(1, newChunkSystemIOThreads);
|
|
- }
|
|
-
|
|
- String newChunkSystemGenParallelism = chunkSystem.genParallelism;
|
|
- if (newChunkSystemGenParallelism.equalsIgnoreCase("default")) {
|
|
- newChunkSystemGenParallelism = "true";
|
|
- }
|
|
-
|
|
- boolean useParallelGen;
|
|
- if (newChunkSystemGenParallelism.equalsIgnoreCase("on") || newChunkSystemGenParallelism.equalsIgnoreCase("enabled")
|
|
- || newChunkSystemGenParallelism.equalsIgnoreCase("true")) {
|
|
- useParallelGen = true;
|
|
- } else if (newChunkSystemGenParallelism.equalsIgnoreCase("off") || newChunkSystemGenParallelism.equalsIgnoreCase("disabled")
|
|
- || newChunkSystemGenParallelism.equalsIgnoreCase("false")) {
|
|
- useParallelGen = false;
|
|
- } else {
|
|
- throw new IllegalStateException("Invalid option for gen-parallelism: must be one of [on, off, enabled, disabled, true, false, default]");
|
|
+ public static void init(final boolean useParallelGen) {
|
|
+ for (final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor executor : MoonriseCommon.RADIUS_AWARE_GROUP.getAllExecutors()) {
|
|
+ executor.setMaxParallelism(useParallelGen ? -1 : 1);
|
|
}
|
|
|
|
- ChunkTaskScheduler.newChunkSystemGenParallelism = MoonriseCommon.WORKER_THREADS;
|
|
- ChunkTaskScheduler.newChunkSystemGenPopulationParallelism = useParallelGen ? MoonriseCommon.WORKER_THREADS : 1;
|
|
- ChunkTaskScheduler.newChunkSystemLoadParallelism = MoonriseCommon.WORKER_THREADS;
|
|
-
|
|
- RegionFileIOThread.init(newChunkSystemIOThreads);
|
|
-
|
|
- LOGGER.info("Chunk system is using " + newChunkSystemIOThreads + " I/O threads, " + MoonriseCommon.WORKER_THREADS + " worker threads, and population gen parallelism of " + ChunkTaskScheduler.newChunkSystemGenPopulationParallelism + " threads");
|
|
+ LOGGER.info("Chunk system is using population gen parallelism: " + useParallelGen);
|
|
}
|
|
|
|
public static final TicketType<Long> CHUNK_LOAD = TicketType.create("chunk_system:chunk_load", Long::compareTo);
|
|
@@ -151,13 +115,15 @@ public final class ChunkTaskScheduler {
|
|
}
|
|
|
|
public final ServerLevel world;
|
|
- public final PrioritisedThreadPool workers;
|
|
public final RadiusAwarePrioritisedExecutor radiusAwareScheduler;
|
|
- public final PrioritisedThreadPool.PrioritisedPoolExecutor parallelGenExecutor;
|
|
- private final PrioritisedThreadPool.PrioritisedPoolExecutor radiusAwareGenExecutor;
|
|
- public final PrioritisedThreadPool.PrioritisedPoolExecutor loadExecutor;
|
|
+ public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor parallelGenExecutor;
|
|
+ private final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor radiusAwareGenExecutor;
|
|
+ public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor loadExecutor;
|
|
+ public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor ioExecutor;
|
|
+ public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor compressionExecutor;
|
|
+ public final PrioritisedThreadPool.ExecutorGroup.ThreadPoolExecutor saveExecutor;
|
|
|
|
- private final PrioritisedThreadedTaskQueue mainThreadExecutor = new PrioritisedThreadedTaskQueue();
|
|
+ private final PrioritisedTaskQueue mainThreadExecutor = new PrioritisedTaskQueue();
|
|
|
|
public final ChunkHolderManager chunkHolderManager;
|
|
|
|
@@ -306,9 +272,8 @@ public final class ChunkTaskScheduler {
|
|
return this.lockShift;
|
|
}
|
|
|
|
- public ChunkTaskScheduler(final ServerLevel world, final PrioritisedThreadPool workers) {
|
|
+ public ChunkTaskScheduler(final ServerLevel world) {
|
|
this.world = world;
|
|
- this.workers = workers;
|
|
// must be >= region shift (in paper, doesn't exist) and must be >= ticket propagator section shift
|
|
// it must be >= region shift since the regioniser assumes ticket updates do not occur in parallel for the region sections
|
|
// it must be >= ticket propagator section shift so that the ticket propagator can assume that owning a position implies owning
|
|
@@ -317,11 +282,14 @@ public final class ChunkTaskScheduler {
|
|
this.lockShift = Math.max(((ChunkSystemServerLevel)world).moonrise$getRegionChunkShift(), ThreadedTicketLevelPropagator.SECTION_SHIFT);
|
|
this.schedulingLockArea = new ReentrantAreaLock(this.getChunkSystemLockShift());
|
|
|
|
- final String worldName = WorldUtil.getWorldName(world);
|
|
- this.parallelGenExecutor = workers.createExecutor("Chunk parallel generation executor for world '" + worldName + "'", 1, Math.max(1, newChunkSystemGenParallelism));
|
|
- this.radiusAwareGenExecutor = workers.createExecutor("Chunk radius aware generator for world '" + worldName + "'", 1, Math.max(1, newChunkSystemGenPopulationParallelism));
|
|
- this.loadExecutor = workers.createExecutor("Chunk load executor for world '" + worldName + "'", 1, newChunkSystemLoadParallelism);
|
|
- this.radiusAwareScheduler = new RadiusAwarePrioritisedExecutor(this.radiusAwareGenExecutor, Math.max(2, 1 + newChunkSystemGenPopulationParallelism));
|
|
+ this.parallelGenExecutor = MoonriseCommon.PARALLEL_GEN_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
|
+ this.radiusAwareGenExecutor = MoonriseCommon.RADIUS_AWARE_GROUP.createExecutor(1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
|
+ this.loadExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
|
+ this.radiusAwareScheduler = new RadiusAwarePrioritisedExecutor(this.radiusAwareGenExecutor, 16);
|
|
+ this.ioExecutor = MoonriseCommon.SERVER_REGION_IO_GROUP.createExecutor(-1, MoonriseCommon.IO_QUEUE_HOLD_TIME, 0);
|
|
+ // we need a separate executor here so that on shutdown we can continue to process I/O tasks
|
|
+ this.compressionExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
|
+ this.saveExecutor = MoonriseCommon.LOAD_GROUP.createExecutor(-1, MoonriseCommon.WORKER_QUEUE_HOLD_TIME, 0);
|
|
this.chunkHolderManager = new ChunkHolderManager(world, this);
|
|
}
|
|
|
|
@@ -360,7 +328,7 @@ public final class ChunkTaskScheduler {
|
|
};
|
|
|
|
// this may not be good enough, specifically thanks to stupid ass plugins swallowing exceptions
|
|
- this.scheduleChunkTask(chunkX, chunkZ, crash, PrioritisedExecutor.Priority.BLOCKING);
|
|
+ this.scheduleChunkTask(chunkX, chunkZ, crash, Priority.BLOCKING);
|
|
// so, make the main thread pick it up
|
|
((ChunkSystemMinecraftServer)this.world.getServer()).moonrise$setChunkSystemCrash(new RuntimeException("Chunk system crash propagated from unrecoverableChunkSystemFailure", reportedException));
|
|
}
|
|
@@ -370,20 +338,20 @@ public final class ChunkTaskScheduler {
|
|
return this.mainThreadExecutor.executeTask();
|
|
}
|
|
|
|
- public void raisePriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
|
|
+ public void raisePriority(final int x, final int z, final Priority priority) {
|
|
this.chunkHolderManager.raisePriority(x, z, priority);
|
|
}
|
|
|
|
- public void setPriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
|
|
+ public void setPriority(final int x, final int z, final Priority priority) {
|
|
this.chunkHolderManager.setPriority(x, z, priority);
|
|
}
|
|
|
|
- public void lowerPriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
|
|
+ public void lowerPriority(final int x, final int z, final Priority priority) {
|
|
this.chunkHolderManager.lowerPriority(x, z, priority);
|
|
}
|
|
|
|
public void scheduleTickingState(final int chunkX, final int chunkZ, final FullChunkStatus toStatus,
|
|
- final boolean addTicket, final PrioritisedExecutor.Priority priority,
|
|
+ final boolean addTicket, final Priority priority,
|
|
final Consumer<LevelChunk> onComplete) {
|
|
final int radius = toStatus.ordinal() - 1; // 0 -> BORDER, 1 -> TICKING, 2 -> ENTITY_TICKING
|
|
|
|
@@ -479,7 +447,7 @@ public final class ChunkTaskScheduler {
|
|
}
|
|
|
|
public void scheduleChunkLoad(final int chunkX, final int chunkZ, final boolean gen, final ChunkStatus toStatus, final boolean addTicket,
|
|
- final PrioritisedExecutor.Priority priority, final Consumer<ChunkAccess> onComplete) {
|
|
+ final Priority priority, final Consumer<ChunkAccess> onComplete) {
|
|
if (gen) {
|
|
this.scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
|
|
return;
|
|
@@ -503,7 +471,7 @@ public final class ChunkTaskScheduler {
|
|
|
|
// only appropriate to use with syncLoadNonFull
|
|
public boolean beginChunkLoadForNonFullSync(final int chunkX, final int chunkZ, final ChunkStatus toStatus,
|
|
- final PrioritisedExecutor.Priority priority) {
|
|
+ final Priority priority) {
|
|
final int accessRadius = getAccessRadius(toStatus);
|
|
final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
|
|
final int minLevel = ChunkTaskScheduler.getTicketLevel(toStatus);
|
|
@@ -548,6 +516,11 @@ public final class ChunkTaskScheduler {
|
|
if (status == null || status.isOrAfter(ChunkStatus.FULL)) {
|
|
throw new IllegalArgumentException("Status: " + status);
|
|
}
|
|
+
|
|
+ if (!TickThread.isTickThread()) {
|
|
+ return this.world.getChunkSource().getChunk(chunkX, chunkZ, status, true);
|
|
+ }
|
|
+
|
|
ChunkAccess loaded = ((ChunkSystemServerLevel)this.world).moonrise$getSpecificChunkIfLoaded(chunkX, chunkZ, status);
|
|
if (loaded != null) {
|
|
return loaded;
|
|
@@ -558,7 +531,7 @@ public final class ChunkTaskScheduler {
|
|
this.chunkHolderManager.addTicketAtLevel(NON_FULL_CHUNK_LOAD, chunkX, chunkZ, ticketLevel, ticketId);
|
|
this.chunkHolderManager.processTicketUpdates();
|
|
|
|
- this.beginChunkLoadForNonFullSync(chunkX, chunkZ, status, PrioritisedExecutor.Priority.BLOCKING);
|
|
+ this.beginChunkLoadForNonFullSync(chunkX, chunkZ, status, Priority.BLOCKING);
|
|
|
|
// we could do a simple spinwait here, since we do not need to process tasks while performing this load
|
|
// but we process tasks only because it's a better use of the time spent
|
|
@@ -578,7 +551,7 @@ public final class ChunkTaskScheduler {
|
|
}
|
|
|
|
public void scheduleChunkLoad(final int chunkX, final int chunkZ, final ChunkStatus toStatus, final boolean addTicket,
|
|
- final PrioritisedExecutor.Priority priority, final Consumer<ChunkAccess> onComplete) {
|
|
+ final Priority priority, final Consumer<ChunkAccess> onComplete) {
|
|
if (!TickThread.isTickThreadFor(this.world, chunkX, chunkZ)) {
|
|
this.scheduleChunkTask(chunkX, chunkZ, () -> {
|
|
ChunkTaskScheduler.this.scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
|
|
@@ -670,7 +643,7 @@ public final class ChunkTaskScheduler {
|
|
|
|
private ChunkProgressionTask createTask(final int chunkX, final int chunkZ, final ChunkAccess chunk,
|
|
final NewChunkHolder chunkHolder, final StaticCache2D<GenerationChunkHolder> neighbours,
|
|
- final ChunkStatus toStatus, final PrioritisedExecutor.Priority initialPriority) {
|
|
+ final ChunkStatus toStatus, final Priority initialPriority) {
|
|
if (toStatus == ChunkStatus.EMPTY) {
|
|
return new ChunkLoadTask(this, this.world, chunkX, chunkZ, chunkHolder, initialPriority);
|
|
}
|
|
@@ -686,7 +659,7 @@ public final class ChunkTaskScheduler {
|
|
|
|
ChunkProgressionTask schedule(final int chunkX, final int chunkZ, final ChunkStatus targetStatus, final NewChunkHolder chunkHolder,
|
|
final List<ChunkProgressionTask> allTasks) {
|
|
- return this.schedule(chunkX, chunkZ, targetStatus, chunkHolder, allTasks, chunkHolder.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL));
|
|
+ return this.schedule(chunkX, chunkZ, targetStatus, chunkHolder, allTasks, chunkHolder.getEffectivePriority(Priority.NORMAL));
|
|
}
|
|
|
|
// rets new task scheduled for the _specified_ chunk
|
|
@@ -695,7 +668,7 @@ public final class ChunkTaskScheduler {
|
|
// schedule will ignore the generation target, so it should be checked by the caller to ensure the target is not regressed!
|
|
private ChunkProgressionTask schedule(final int chunkX, final int chunkZ, final ChunkStatus targetStatus,
|
|
final NewChunkHolder chunkHolder, final List<ChunkProgressionTask> allTasks,
|
|
- final PrioritisedExecutor.Priority minPriority) {
|
|
+ final Priority minPriority) {
|
|
if (!this.schedulingLockArea.isHeldByCurrentThread(chunkX, chunkZ, getAccessRadius(targetStatus))) {
|
|
throw new IllegalStateException("Not holding scheduling lock");
|
|
}
|
|
@@ -705,8 +678,8 @@ public final class ChunkTaskScheduler {
|
|
return null;
|
|
}
|
|
|
|
- final PrioritisedExecutor.Priority requestedPriority = PrioritisedExecutor.Priority.max(
|
|
- minPriority, chunkHolder.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL)
|
|
+ final Priority requestedPriority = Priority.max(
|
|
+ minPriority, chunkHolder.getEffectivePriority(Priority.NORMAL)
|
|
);
|
|
final ChunkStatus currentGenStatus = chunkHolder.getCurrentGenStatus();
|
|
final ChunkAccess chunk = chunkHolder.getCurrentChunk();
|
|
@@ -743,7 +716,7 @@ public final class ChunkTaskScheduler {
|
|
|
|
final int neighbourReadRadius = Math.max(
|
|
0,
|
|
- chunkPyramid.getStepTo(toStatus).getAccumulatedRadiusOf(ChunkStatus.EMPTY)
|
|
+ chunkStep.getAccumulatedRadiusOf(ChunkStatus.EMPTY)
|
|
);
|
|
|
|
boolean unGeneratedNeighbours = false;
|
|
@@ -783,7 +756,7 @@ public final class ChunkTaskScheduler {
|
|
|
|
final ChunkProgressionTask task = this.createTask(
|
|
chunkX, chunkZ, chunk, chunkHolder, neighbours, toStatus,
|
|
- chunkHolder.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL)
|
|
+ chunkHolder.getEffectivePriority(Priority.NORMAL)
|
|
);
|
|
allTasks.add(task);
|
|
|
|
@@ -794,7 +767,7 @@ public final class ChunkTaskScheduler {
|
|
|
|
// rets true if the neighbour is not at the required status, false otherwise
|
|
private boolean checkNeighbour(final int chunkX, final int chunkZ, final ChunkStatus requiredStatus, final NewChunkHolder center,
|
|
- final List<ChunkProgressionTask> tasks, final PrioritisedExecutor.Priority minPriority) {
|
|
+ final List<ChunkProgressionTask> tasks, final Priority minPriority) {
|
|
final NewChunkHolder chunkHolder = this.chunkHolderManager.getChunkHolder(chunkX, chunkZ);
|
|
|
|
if (chunkHolder == null) {
|
|
@@ -830,41 +803,41 @@ public final class ChunkTaskScheduler {
|
|
*/
|
|
@Deprecated
|
|
public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final Runnable run) {
|
|
- return this.scheduleChunkTask(run, PrioritisedExecutor.Priority.NORMAL);
|
|
+ return this.scheduleChunkTask(run, Priority.NORMAL);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Chunk tasks must be tied to coordinates in the future
|
|
*/
|
|
@Deprecated
|
|
- public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
- return this.mainThreadExecutor.queueRunnable(run, priority);
|
|
+ public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final Runnable run, final Priority priority) {
|
|
+ return this.mainThreadExecutor.queueTask(run, priority);
|
|
}
|
|
|
|
public PrioritisedExecutor.PrioritisedTask createChunkTask(final int chunkX, final int chunkZ, final Runnable run) {
|
|
- return this.createChunkTask(chunkX, chunkZ, run, PrioritisedExecutor.Priority.NORMAL);
|
|
+ return this.createChunkTask(chunkX, chunkZ, run, Priority.NORMAL);
|
|
}
|
|
|
|
public PrioritisedExecutor.PrioritisedTask createChunkTask(final int chunkX, final int chunkZ, final Runnable run,
|
|
- final PrioritisedExecutor.Priority priority) {
|
|
+ final Priority priority) {
|
|
return this.mainThreadExecutor.createTask(run, priority);
|
|
}
|
|
|
|
public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final int chunkX, final int chunkZ, final Runnable run) {
|
|
- return this.scheduleChunkTask(chunkX, chunkZ, run, PrioritisedExecutor.Priority.NORMAL);
|
|
+ return this.scheduleChunkTask(chunkX, chunkZ, run, Priority.NORMAL);
|
|
}
|
|
|
|
public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final int chunkX, final int chunkZ, final Runnable run,
|
|
- final PrioritisedExecutor.Priority priority) {
|
|
- return this.mainThreadExecutor.queueRunnable(run, priority);
|
|
+ final Priority priority) {
|
|
+ return this.mainThreadExecutor.queueTask(run, priority);
|
|
}
|
|
|
|
public boolean halt(final boolean sync, final long maxWaitNS) {
|
|
this.radiusAwareGenExecutor.halt();
|
|
this.parallelGenExecutor.halt();
|
|
this.loadExecutor.halt();
|
|
- final long time = System.nanoTime();
|
|
if (sync) {
|
|
+ final long time = System.nanoTime();
|
|
for (long failures = 9L;; failures = ConcurrentUtil.linearLongBackoff(failures, 500_000L, 50_000_000L)) {
|
|
if (
|
|
!this.radiusAwareGenExecutor.isActive() &&
|
|
@@ -882,6 +855,29 @@ public final class ChunkTaskScheduler {
|
|
return true;
|
|
}
|
|
|
|
+ public boolean haltIO(final boolean sync, final long maxWaitNS) {
|
|
+ this.ioExecutor.halt();
|
|
+ this.saveExecutor.halt();
|
|
+ this.compressionExecutor.halt();
|
|
+ if (sync) {
|
|
+ final long time = System.nanoTime();
|
|
+ for (long failures = 9L;; failures = ConcurrentUtil.linearLongBackoff(failures, 500_000L, 50_000_000L)) {
|
|
+ if (
|
|
+ !this.ioExecutor.isActive() &&
|
|
+ !this.saveExecutor.isActive() &&
|
|
+ !this.compressionExecutor.isActive()
|
|
+ ) {
|
|
+ return true;
|
|
+ }
|
|
+ if ((System.nanoTime() - time) >= maxWaitNS) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
public static final ArrayDeque<ChunkInfo> WAITING_CHUNKS = new ArrayDeque<>(); // stack
|
|
|
|
public static final class ChunkInfo {
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java
|
|
index 45eda96fd8a1acb87dbb69ce5495fec7e451416f..381631e405895ba3eede1cd2e1011c64aadbd662 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java
|
|
@@ -1,18 +1,20 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
|
|
|
|
-import ca.spottedleaf.concurrentutil.completable.Completable;
|
|
+import ca.spottedleaf.concurrentutil.completable.CallbackCompletable;
|
|
import ca.spottedleaf.concurrentutil.executor.Cancellable;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.DelayedPrioritisedTask;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
+import ca.spottedleaf.moonrise.common.misc.LazyRunnable;
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
import ca.spottedleaf.moonrise.common.util.TickThread;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
import ca.spottedleaf.moonrise.common.util.ChunkSystem;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemFeatures;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.async_save.AsyncChunkSaveData;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkStatus;
|
|
@@ -36,13 +38,14 @@ import net.minecraft.server.level.ChunkHolder;
|
|
import net.minecraft.server.level.ChunkLevel;
|
|
import net.minecraft.server.level.FullChunkStatus;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
+import net.minecraft.server.level.progress.ChunkProgressListener;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
|
import net.minecraft.world.level.chunk.LevelChunk;
|
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
-import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
|
+import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import java.lang.invoke.VarHandle;
|
|
@@ -58,6 +61,8 @@ public final class NewChunkHolder {
|
|
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(NewChunkHolder.class);
|
|
|
|
+ public final ChunkData holderData;
|
|
+
|
|
public final ServerLevel world;
|
|
public final int chunkX;
|
|
public final int chunkZ;
|
|
@@ -89,7 +94,7 @@ public final class NewChunkHolder {
|
|
if (this.entityChunk == null) {
|
|
ret = this.entityChunk = new ChunkEntitySlices(
|
|
this.world, this.chunkX, this.chunkZ, this.getChunkStatus(),
|
|
- WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
|
+ this.holderData, WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
|
|
);
|
|
|
|
ret.setTransient(transientChunk);
|
|
@@ -173,7 +178,7 @@ public final class NewChunkHolder {
|
|
// no tasks to schedule _for_
|
|
} else {
|
|
entityDataLoadTask = this.entityDataLoadTask = new ChunkLoadTask.EntityDataLoadTask(
|
|
- this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL)
|
|
+ this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority(Priority.NORMAL)
|
|
);
|
|
entityDataLoadTask.addCallback(this::completeEntityLoad);
|
|
// need one schedule() per waiter
|
|
@@ -220,7 +225,7 @@ public final class NewChunkHolder {
|
|
|
|
if (this.entityDataLoadTask == null) {
|
|
this.entityDataLoadTask = new ChunkLoadTask.EntityDataLoadTask(
|
|
- this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL)
|
|
+ this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority(Priority.NORMAL)
|
|
);
|
|
this.entityDataLoadTask.addCallback(this::completeEntityLoad);
|
|
this.entityDataLoadTaskWaiters = new ArrayList<>();
|
|
@@ -294,7 +299,7 @@ public final class NewChunkHolder {
|
|
// no tasks to schedule _for_
|
|
} else {
|
|
poiDataLoadTask = this.poiDataLoadTask = new ChunkLoadTask.PoiDataLoadTask(
|
|
- this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL)
|
|
+ this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority(Priority.NORMAL)
|
|
);
|
|
poiDataLoadTask.addCallback(this::completePoiLoad);
|
|
// need one schedule() per waiter
|
|
@@ -340,7 +345,7 @@ public final class NewChunkHolder {
|
|
|
|
if (this.poiDataLoadTask == null) {
|
|
this.poiDataLoadTask = new ChunkLoadTask.PoiDataLoadTask(
|
|
- this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL)
|
|
+ this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority(Priority.NORMAL)
|
|
);
|
|
this.poiDataLoadTask.addCallback(this::completePoiLoad);
|
|
this.poiDataLoadTaskWaiters = new ArrayList<>();
|
|
@@ -519,15 +524,15 @@ public final class NewChunkHolder {
|
|
// priority state
|
|
|
|
// the target priority for this chunk to generate at
|
|
- private PrioritisedExecutor.Priority priority = null;
|
|
+ private Priority priority = null;
|
|
private boolean priorityLocked;
|
|
|
|
// the priority neighbouring chunks have requested this chunk generate at
|
|
- private PrioritisedExecutor.Priority neighbourRequestedPriority = null;
|
|
+ private Priority neighbourRequestedPriority = null;
|
|
|
|
- public PrioritisedExecutor.Priority getEffectivePriority(final PrioritisedExecutor.Priority dfl) {
|
|
- final PrioritisedExecutor.Priority neighbour = this.neighbourRequestedPriority;
|
|
- final PrioritisedExecutor.Priority us = this.priority;
|
|
+ public Priority getEffectivePriority(final Priority dfl) {
|
|
+ final Priority neighbour = this.neighbourRequestedPriority;
|
|
+ final Priority us = this.priority;
|
|
|
|
if (neighbour == null) {
|
|
return us == null ? dfl : us;
|
|
@@ -536,7 +541,7 @@ public final class NewChunkHolder {
|
|
return neighbour;
|
|
}
|
|
|
|
- return PrioritisedExecutor.Priority.max(us, neighbour);
|
|
+ return Priority.max(us, neighbour);
|
|
}
|
|
|
|
private void recalculateNeighbourRequestedPriority() {
|
|
@@ -545,18 +550,18 @@ public final class NewChunkHolder {
|
|
return;
|
|
}
|
|
|
|
- PrioritisedExecutor.Priority max = null;
|
|
+ Priority max = null;
|
|
|
|
for (final NewChunkHolder holder : this.neighboursWaitingForUs.keySet()) {
|
|
- final PrioritisedExecutor.Priority neighbourPriority = holder.getEffectivePriority(null);
|
|
+ final Priority neighbourPriority = holder.getEffectivePriority(null);
|
|
if (neighbourPriority != null && (max == null || neighbourPriority.isHigherPriority(max))) {
|
|
max = neighbourPriority;
|
|
}
|
|
}
|
|
|
|
- final PrioritisedExecutor.Priority current = this.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL);
|
|
+ final Priority current = this.getEffectivePriority(Priority.NORMAL);
|
|
this.neighbourRequestedPriority = max;
|
|
- final PrioritisedExecutor.Priority next = this.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL);
|
|
+ final Priority next = this.getEffectivePriority(Priority.NORMAL);
|
|
|
|
if (current == next) {
|
|
return;
|
|
@@ -578,7 +583,7 @@ public final class NewChunkHolder {
|
|
}
|
|
|
|
// must hold scheduling lock
|
|
- public void raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void raisePriority(final Priority priority) {
|
|
if (this.priority != null && this.priority.isHigherOrEqualPriority(priority)) {
|
|
return;
|
|
}
|
|
@@ -591,13 +596,13 @@ public final class NewChunkHolder {
|
|
}
|
|
|
|
// must hold scheduling lock
|
|
- public void setPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void setPriority(final Priority priority) {
|
|
if (this.priorityLocked) {
|
|
return;
|
|
}
|
|
- final PrioritisedExecutor.Priority old = this.getEffectivePriority(null);
|
|
+ final Priority old = this.getEffectivePriority(null);
|
|
this.priority = priority;
|
|
- final PrioritisedExecutor.Priority newPriority = this.getEffectivePriority(PrioritisedExecutor.Priority.NORMAL);
|
|
+ final Priority newPriority = this.getEffectivePriority(Priority.NORMAL);
|
|
|
|
if (old != newPriority) {
|
|
if (this.generationTask != null) {
|
|
@@ -609,7 +614,7 @@ public final class NewChunkHolder {
|
|
}
|
|
|
|
// must hold scheduling lock
|
|
- public void lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void lowerPriority(final Priority priority) {
|
|
if (this.priority != null && this.priority.isLowerOrEqualPriority(priority)) {
|
|
return;
|
|
}
|
|
@@ -632,7 +637,7 @@ public final class NewChunkHolder {
|
|
}
|
|
|
|
// ticket level state
|
|
- public int oldTicketLevel = ChunkHolderManager.MAX_TICKET_LEVEL + 1;
|
|
+ private int oldTicketLevel = ChunkHolderManager.MAX_TICKET_LEVEL + 1;
|
|
private int currentTicketLevel = ChunkHolderManager.MAX_TICKET_LEVEL + 1;
|
|
|
|
public int getTicketLevel() {
|
|
@@ -651,6 +656,7 @@ public final class NewChunkHolder {
|
|
world.getLightEngine(), null, world.getChunkSource().chunkMap
|
|
);
|
|
((ChunkSystemChunkHolder)this.vanillaChunkHolder).moonrise$setRealChunkHolder(this);
|
|
+ this.holderData = ((ChunkSystemLevel)this.world).moonrise$requestChunkData(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
}
|
|
|
|
public ChunkAccess getCurrentChunk() {
|
|
@@ -750,8 +756,9 @@ public final class NewChunkHolder {
|
|
/** Unloaded from chunk map */
|
|
private boolean unloaded;
|
|
|
|
- void markUnloaded() {
|
|
+ void onUnload() {
|
|
this.unloaded = true;
|
|
+ ((ChunkSystemLevel)this.world).moonrise$releaseChunkData(CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ));
|
|
}
|
|
|
|
private boolean inUnloadQueue = false;
|
|
@@ -788,9 +795,10 @@ public final class NewChunkHolder {
|
|
private UnloadTask entityDataUnload;
|
|
private UnloadTask poiDataUnload;
|
|
|
|
- public static final record UnloadTask(Completable<CompoundTag> completable, DelayedPrioritisedTask task) {}
|
|
+ public static final record UnloadTask(CallbackCompletable<CompoundTag> completable, PrioritisedExecutor.PrioritisedTask task,
|
|
+ LazyRunnable toRun) {}
|
|
|
|
- public UnloadTask getUnloadTask(final RegionFileIOThread.RegionFileType type) {
|
|
+ public UnloadTask getUnloadTask(final MoonriseRegionFileIO.RegionFileType type) {
|
|
switch (type) {
|
|
case CHUNK_DATA:
|
|
return this.chunkDataUnload;
|
|
@@ -803,7 +811,7 @@ public final class NewChunkHolder {
|
|
}
|
|
}
|
|
|
|
- private void removeUnloadTask(final RegionFileIOThread.RegionFileType type) {
|
|
+ private void removeUnloadTask(final MoonriseRegionFileIO.RegionFileType type) {
|
|
switch (type) {
|
|
case CHUNK_DATA: {
|
|
this.chunkDataUnload = null;
|
|
@@ -836,10 +844,10 @@ public final class NewChunkHolder {
|
|
// chunk state
|
|
this.currentChunk = null;
|
|
this.currentGenStatus = null;
|
|
- this.lastChunkCompletion = null;
|
|
for (int i = 0; i < this.chunkCompletions.length; ++i) {
|
|
- CHUNK_COMPLETION_ARRAY_HANDLE.setVolatile(this.chunkCompletions, i, (ChunkCompletion)null);
|
|
+ CHUNK_COMPLETION_ARRAY_HANDLE.setRelease(this.chunkCompletions, i, (ChunkCompletion)null);
|
|
}
|
|
+ this.lastChunkCompletion = null;
|
|
// entity chunk state
|
|
this.entityChunk = null;
|
|
this.pendingEntityChunk = null;
|
|
@@ -851,22 +859,23 @@ public final class NewChunkHolder {
|
|
this.priorityLocked = false;
|
|
|
|
if (chunk != null) {
|
|
- this.chunkDataUnload = new UnloadTask(new Completable<>(), new DelayedPrioritisedTask(PrioritisedExecutor.Priority.NORMAL));
|
|
+ final LazyRunnable toRun = new LazyRunnable();
|
|
+ this.chunkDataUnload = new UnloadTask(new CallbackCompletable<>(), this.scheduler.saveExecutor.createTask(toRun), toRun);
|
|
}
|
|
if (poiChunk != null) {
|
|
- this.poiDataUnload = new UnloadTask(new Completable<>(), null);
|
|
+ this.poiDataUnload = new UnloadTask(new CallbackCompletable<>(), null, null);
|
|
}
|
|
if (entityChunk != null) {
|
|
- this.entityDataUnload = new UnloadTask(new Completable<>(), null);
|
|
+ this.entityDataUnload = new UnloadTask(new CallbackCompletable<>(), null, null);
|
|
}
|
|
|
|
return this.unloadState = (chunk != null || entityChunk != null || poiChunk != null) ? new UnloadState(this, chunk, entityChunk, poiChunk) : null;
|
|
}
|
|
|
|
// data is null if failed or does not need to be saved
|
|
- void completeAsyncUnloadDataSave(final RegionFileIOThread.RegionFileType type, final CompoundTag data) {
|
|
+ void completeAsyncUnloadDataSave(final MoonriseRegionFileIO.RegionFileType type, final CompoundTag data) {
|
|
if (data != null) {
|
|
- RegionFileIOThread.scheduleSave(this.world, this.chunkX, this.chunkZ, data, type);
|
|
+ MoonriseRegionFileIO.scheduleSave(this.world, this.chunkX, this.chunkZ, data, type);
|
|
}
|
|
|
|
this.getUnloadTask(type).completable().complete(data);
|
|
@@ -886,18 +895,19 @@ public final class NewChunkHolder {
|
|
final ChunkEntitySlices entityChunk = state.entityChunk();
|
|
final PoiChunk poiChunk = state.poiChunk();
|
|
|
|
- final boolean shouldLevelChunkNotSave = ChunkSystemFeatures.forceNoSave(chunk);
|
|
+ final boolean shouldLevelChunkNotSave = PlatformHooks.get().forceNoSave(chunk);
|
|
|
|
// unload chunk data
|
|
if (chunk != null) {
|
|
if (chunk instanceof LevelChunk levelChunk) {
|
|
levelChunk.setLoaded(false);
|
|
+ PlatformHooks.get().chunkUnloadFromWorld(levelChunk);
|
|
}
|
|
|
|
if (!shouldLevelChunkNotSave) {
|
|
this.saveChunk(chunk, true);
|
|
} else {
|
|
- this.completeAsyncUnloadDataSave(RegionFileIOThread.RegionFileType.CHUNK_DATA, null);
|
|
+ this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, null);
|
|
}
|
|
|
|
if (chunk instanceof LevelChunk levelChunk) {
|
|
@@ -1066,6 +1076,9 @@ public final class NewChunkHolder {
|
|
if (oldUnloaded != newUnloaded) {
|
|
this.checkUnload();
|
|
}
|
|
+
|
|
+ // Don't really have a choice but to place this hook here
|
|
+ PlatformHooks.get().onChunkHolderTicketChange(this.world, this, oldLevel, newLevel);
|
|
}
|
|
|
|
static final int NEIGHBOUR_RADIUS = 2;
|
|
@@ -1111,24 +1124,6 @@ public final class NewChunkHolder {
|
|
private static final long CHUNK_LOADED_MASK_RAD1 = getLoadedMask(1);
|
|
private static final long CHUNK_LOADED_MASK_RAD2 = getLoadedMask(2);
|
|
|
|
- public static boolean areNeighboursFullLoaded(final long bitset, final int radius) {
|
|
- switch (radius) {
|
|
- case 0: {
|
|
- return (bitset & CHUNK_LOADED_MASK_RAD0) == CHUNK_LOADED_MASK_RAD0;
|
|
- }
|
|
- case 1: {
|
|
- return (bitset & CHUNK_LOADED_MASK_RAD1) == CHUNK_LOADED_MASK_RAD1;
|
|
- }
|
|
- case 2: {
|
|
- return (bitset & CHUNK_LOADED_MASK_RAD2) == CHUNK_LOADED_MASK_RAD2;
|
|
- }
|
|
-
|
|
- default: {
|
|
- throw new IllegalArgumentException("Radius not recognized: " + radius);
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
// only updated while holding scheduling lock
|
|
private FullChunkStatus pendingFullChunkStatus = FullChunkStatus.INACCESSIBLE;
|
|
// updated while holding no locks, but adds a ticket before to prevent pending status from dropping
|
|
@@ -1363,6 +1358,17 @@ public final class NewChunkHolder {
|
|
}
|
|
|
|
private void completeStatusConsumers(ChunkStatus status, final ChunkAccess chunk) {
|
|
+ // Update progress listener for LevelLoadingScreen
|
|
+ if (chunk != null) {
|
|
+ final ChunkProgressListener progressListener = this.world.getChunkSource().chunkMap.progressListener;
|
|
+ if (progressListener != null) {
|
|
+ final ChunkStatus finalStatus = status;
|
|
+ this.scheduler.scheduleChunkTask(this.chunkX, this.chunkZ, () -> {
|
|
+ progressListener.onStatusChange(this.vanillaChunkHolder.getPos(), finalStatus);
|
|
+ });
|
|
+ }
|
|
+ }
|
|
+
|
|
// need to tell future statuses to complete if cancelled
|
|
do {
|
|
this.completeStatusConsumers0(status, chunk);
|
|
@@ -1386,7 +1392,7 @@ public final class NewChunkHolder {
|
|
LOGGER.error("Failed to process chunk status callback", thr);
|
|
}
|
|
}
|
|
- }, PrioritisedExecutor.Priority.HIGHEST);
|
|
+ }, Priority.HIGHEST);
|
|
}
|
|
|
|
private final Reference2ObjectOpenHashMap<FullChunkStatus, List<Consumer<LevelChunk>>> fullStatusWaiters = new Reference2ObjectOpenHashMap<>();
|
|
@@ -1414,7 +1420,7 @@ public final class NewChunkHolder {
|
|
LOGGER.error("Failed to process chunk status callback", thr);
|
|
}
|
|
}
|
|
- }, PrioritisedExecutor.Priority.HIGHEST);
|
|
+ }, Priority.HIGHEST);
|
|
}
|
|
|
|
// note: must hold scheduling lock
|
|
@@ -1670,6 +1676,8 @@ public final class NewChunkHolder {
|
|
|
|
public static final record SaveStat(boolean savedChunk, boolean savedEntityChunk, boolean savedPoiChunk) {}
|
|
|
|
+ private static final MoonriseRegionFileIO.RegionFileType[] REGION_FILE_TYPES = MoonriseRegionFileIO.RegionFileType.values();
|
|
+
|
|
public SaveStat save(final boolean shutdown) {
|
|
TickThread.ensureTickThread(this.world, this.chunkX, this.chunkZ, "Cannot save data off-main");
|
|
|
|
@@ -1677,6 +1685,7 @@ public final class NewChunkHolder {
|
|
PoiChunk poi = this.getPoiChunk();
|
|
ChunkEntitySlices entities = this.getEntityChunk();
|
|
boolean executedUnloadTask = false;
|
|
+ final boolean[] executedUnloadTasks = new boolean[REGION_FILE_TYPES.length];
|
|
|
|
if (shutdown) {
|
|
// make sure that the async unloads complete
|
|
@@ -1686,17 +1695,22 @@ public final class NewChunkHolder {
|
|
poi = this.unloadState.poiChunk();
|
|
entities = this.unloadState.entityChunk();
|
|
}
|
|
- final UnloadTask chunkUnloadTask = this.chunkDataUnload;
|
|
- final DelayedPrioritisedTask chunkDataUnloadTask = chunkUnloadTask == null ? null : chunkUnloadTask.task();
|
|
- if (chunkDataUnloadTask != null) {
|
|
- final PrioritisedExecutor.PrioritisedTask unloadTask = chunkDataUnloadTask.getTask();
|
|
- if (unloadTask != null) {
|
|
- executedUnloadTask = unloadTask.execute();
|
|
+ for (final MoonriseRegionFileIO.RegionFileType regionFileType : REGION_FILE_TYPES) {
|
|
+ final UnloadTask unloadTask = this.getUnloadTask(regionFileType);
|
|
+ if (unloadTask == null) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ final PrioritisedExecutor.PrioritisedTask task = unloadTask.task();
|
|
+ if (task != null && task.isQueued()) {
|
|
+ final boolean executed = task.execute();
|
|
+ executedUnloadTask |= executed;
|
|
+ executedUnloadTasks[regionFileType.ordinal()] = executed;
|
|
}
|
|
}
|
|
}
|
|
|
|
- final boolean forceNoSaveChunk = ChunkSystemFeatures.forceNoSave(chunk);
|
|
+ final boolean forceNoSaveChunk = PlatformHooks.get().forceNoSave(chunk);
|
|
|
|
// can only synchronously save worldgen chunks during shutdown
|
|
boolean canSaveChunk = !forceNoSaveChunk && (chunk != null && ((shutdown || chunk instanceof LevelChunk) && chunk.isUnsaved()));
|
|
@@ -1717,106 +1731,55 @@ public final class NewChunkHolder {
|
|
}
|
|
}
|
|
|
|
- return executedUnloadTask | canSaveChunk | canSaveEntities | canSavePOI ? new SaveStat(executedUnloadTask || canSaveChunk, canSaveEntities, canSavePOI): null;
|
|
- }
|
|
-
|
|
- static final class AsyncChunkSerializeTask implements Runnable {
|
|
-
|
|
- private final ServerLevel world;
|
|
- private final ChunkAccess chunk;
|
|
- private final AsyncChunkSaveData asyncSaveData;
|
|
- private final NewChunkHolder toComplete;
|
|
-
|
|
- public AsyncChunkSerializeTask(final ServerLevel world, final ChunkAccess chunk, final AsyncChunkSaveData asyncSaveData,
|
|
- final NewChunkHolder toComplete) {
|
|
- this.world = world;
|
|
- this.chunk = chunk;
|
|
- this.asyncSaveData = asyncSaveData;
|
|
- this.toComplete = toComplete;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void run() {
|
|
- final CompoundTag toSerialize;
|
|
- try {
|
|
- toSerialize = ChunkSystemFeatures.saveChunkAsync(this.world, this.chunk, this.asyncSaveData);
|
|
- } catch (final Throwable throwable) {
|
|
- LOGGER.error("Failed to asynchronously save chunk " + this.chunk.getPos() + " for world '" + WorldUtil.getWorldName(this.world) + "', falling back to synchronous save", throwable);
|
|
- final ChunkPos pos = this.chunk.getPos();
|
|
- ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().scheduleChunkTask(pos.x, pos.z, () -> {
|
|
- final CompoundTag synchronousSave;
|
|
- try {
|
|
- synchronousSave = ChunkSystemFeatures.saveChunkAsync(AsyncChunkSerializeTask.this.world, AsyncChunkSerializeTask.this.chunk, AsyncChunkSerializeTask.this.asyncSaveData);
|
|
- } catch (final Throwable throwable2) {
|
|
- LOGGER.error("Failed to synchronously save chunk " + AsyncChunkSerializeTask.this.chunk.getPos() + " for world '" + WorldUtil.getWorldName(AsyncChunkSerializeTask.this.world) + "', chunk data will be lost", throwable2);
|
|
- AsyncChunkSerializeTask.this.toComplete.completeAsyncUnloadDataSave(RegionFileIOThread.RegionFileType.CHUNK_DATA, null);
|
|
- return;
|
|
- }
|
|
-
|
|
- AsyncChunkSerializeTask.this.toComplete.completeAsyncUnloadDataSave(RegionFileIOThread.RegionFileType.CHUNK_DATA, synchronousSave);
|
|
- LOGGER.info("Successfully serialized chunk " + AsyncChunkSerializeTask.this.chunk.getPos() + " for world '" + WorldUtil.getWorldName(AsyncChunkSerializeTask.this.world) + "' synchronously");
|
|
-
|
|
- }, PrioritisedExecutor.Priority.HIGHEST);
|
|
- return;
|
|
- }
|
|
- this.toComplete.completeAsyncUnloadDataSave(RegionFileIOThread.RegionFileType.CHUNK_DATA, toSerialize);
|
|
- }
|
|
-
|
|
- @Override
|
|
- public String toString() {
|
|
- return "AsyncChunkSerializeTask{" +
|
|
- "chunk={pos=" + this.chunk.getPos() + ",world=\"" + WorldUtil.getWorldName(this.world) + "\"}" +
|
|
- "}";
|
|
- }
|
|
+ return executedUnloadTask | canSaveChunk | canSaveEntities | canSavePOI ?
|
|
+ new SaveStat(
|
|
+ canSaveChunk | executedUnloadTasks[MoonriseRegionFileIO.RegionFileType.CHUNK_DATA.ordinal()],
|
|
+ canSaveEntities | executedUnloadTasks[MoonriseRegionFileIO.RegionFileType.ENTITY_DATA.ordinal()],
|
|
+ canSavePOI | executedUnloadTasks[MoonriseRegionFileIO.RegionFileType.POI_DATA.ordinal()]
|
|
+ )
|
|
+ : null;
|
|
}
|
|
|
|
private boolean saveChunk(final ChunkAccess chunk, final boolean unloading) {
|
|
if (!chunk.isUnsaved()) {
|
|
if (unloading) {
|
|
- this.completeAsyncUnloadDataSave(RegionFileIOThread.RegionFileType.CHUNK_DATA, null);
|
|
+ this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, null);
|
|
}
|
|
return false;
|
|
}
|
|
- boolean completing = false;
|
|
- boolean failedAsyncPrepare = false;
|
|
try {
|
|
- if (unloading && ChunkSystemFeatures.supportsAsyncChunkSave()) {
|
|
- try {
|
|
- final AsyncChunkSaveData asyncSaveData = ChunkSystemFeatures.getAsyncSaveData(this.world, chunk);
|
|
+ final SerializableChunkData chunkData = SerializableChunkData.copyOf(this.world, chunk);
|
|
+ PlatformHooks.get().chunkSyncSave(this.world, chunk, chunkData);
|
|
|
|
- final PrioritisedExecutor.PrioritisedTask task = this.scheduler.loadExecutor.createTask(new AsyncChunkSerializeTask(this.world, chunk, asyncSaveData, this));
|
|
+ chunk.tryMarkSaved();
|
|
|
|
- this.chunkDataUnload.task().setTask(task);
|
|
+ final CallbackCompletable<CompoundTag> completable = new CallbackCompletable<>();
|
|
|
|
- chunk.setUnsaved(false);
|
|
+ final Runnable run = () -> {
|
|
+ final CompoundTag data = chunkData.write();
|
|
|
|
- task.queue();
|
|
+ completable.complete(data);
|
|
|
|
- return true;
|
|
- } catch (final Throwable thr) {
|
|
- LOGGER.error("Failed to prepare async chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "', falling back to synchronous save", thr);
|
|
- failedAsyncPrepare = true;
|
|
- // fall through to synchronous save
|
|
+ if (unloading) {
|
|
+ NewChunkHolder.this.completeAsyncUnloadDataSave(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, data);
|
|
}
|
|
- }
|
|
-
|
|
- final CompoundTag save = ChunkSerializer.write(this.world, chunk);
|
|
+ };
|
|
|
|
+ final PrioritisedExecutor.PrioritisedTask task;
|
|
if (unloading) {
|
|
- completing = true;
|
|
- this.completeAsyncUnloadDataSave(RegionFileIOThread.RegionFileType.CHUNK_DATA, save);
|
|
- if (failedAsyncPrepare) {
|
|
- LOGGER.info("Successfully serialized chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "' synchronously");
|
|
- }
|
|
+ this.chunkDataUnload.toRun().setRunnable(run);
|
|
+ task = this.chunkDataUnload.task();
|
|
} else {
|
|
- RegionFileIOThread.scheduleSave(this.world, this.chunkX, this.chunkZ, save, RegionFileIOThread.RegionFileType.CHUNK_DATA);
|
|
+ task = this.scheduler.saveExecutor.createTask(run);
|
|
}
|
|
- chunk.setUnsaved(false);
|
|
+
|
|
+ task.queue();
|
|
+
|
|
+ MoonriseRegionFileIO.scheduleSave(
|
|
+ this.world, this.chunkX, this.chunkZ, completable, task, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, Priority.NORMAL
|
|
+ );
|
|
} catch (final Throwable thr) {
|
|
LOGGER.error("Failed to save chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "'", thr);
|
|
- if (unloading && !completing) {
|
|
- this.completeAsyncUnloadDataSave(RegionFileIOThread.RegionFileType.CHUNK_DATA, null);
|
|
- }
|
|
}
|
|
|
|
return true;
|
|
@@ -1834,7 +1797,7 @@ public final class NewChunkHolder {
|
|
return false;
|
|
}
|
|
try {
|
|
- mergeFrom = RegionFileIOThread.loadData(this.world, this.chunkX, this.chunkZ, RegionFileIOThread.RegionFileType.ENTITY_DATA, PrioritisedExecutor.Priority.BLOCKING);
|
|
+ mergeFrom = MoonriseRegionFileIO.loadData(this.world, this.chunkX, this.chunkZ, MoonriseRegionFileIO.RegionFileType.ENTITY_DATA, Priority.BLOCKING);
|
|
} catch (final Exception ex) {
|
|
LOGGER.error("Cannot merge transient entities for chunk (" + this.chunkX + "," + this.chunkZ + ") in world '" + WorldUtil.getWorldName(this.world) + "', data on disk will be replaced", ex);
|
|
}
|
|
@@ -1853,7 +1816,7 @@ public final class NewChunkHolder {
|
|
return false;
|
|
}
|
|
|
|
- RegionFileIOThread.scheduleSave(this.world, this.chunkX, this.chunkZ, save, RegionFileIOThread.RegionFileType.ENTITY_DATA);
|
|
+ MoonriseRegionFileIO.scheduleSave(this.world, this.chunkX, this.chunkZ, save, MoonriseRegionFileIO.RegionFileType.ENTITY_DATA);
|
|
this.lastEntitySaveNull = save == null;
|
|
if (unloading) {
|
|
this.lastEntityUnload = save;
|
|
@@ -1877,7 +1840,7 @@ public final class NewChunkHolder {
|
|
return false;
|
|
}
|
|
|
|
- RegionFileIOThread.scheduleSave(this.world, this.chunkX, this.chunkZ, save, RegionFileIOThread.RegionFileType.POI_DATA);
|
|
+ MoonriseRegionFileIO.scheduleSave(this.world, this.chunkX, this.chunkZ, save, MoonriseRegionFileIO.RegionFileType.POI_DATA);
|
|
this.lastPoiSaveNull = save == null;
|
|
if (unloading) {
|
|
this.poiDataUnload.completable().complete(save);
|
|
@@ -1924,7 +1887,7 @@ public final class NewChunkHolder {
|
|
return element == null ? JsonNull.INSTANCE : new JsonPrimitive(element.toString());
|
|
}
|
|
|
|
- private static JsonObject serializeCompletable(final Completable<?> completable) {
|
|
+ private static JsonObject serializeCompletable(final CallbackCompletable<?> completable) {
|
|
final JsonObject ret = new JsonObject();
|
|
|
|
if (completable == null) {
|
|
@@ -2019,13 +1982,13 @@ public final class NewChunkHolder {
|
|
ret.add("poi_unload_completable", serializeCompletable(poiDataUnload == null ? null : poiDataUnload.completable()));
|
|
ret.add("chunk_unload_completable", serializeCompletable(chunkDataUnload == null ? null : chunkDataUnload.completable()));
|
|
|
|
- final DelayedPrioritisedTask unloadTask = chunkDataUnload == null ? null : chunkDataUnload.task();
|
|
+ final PrioritisedExecutor.PrioritisedTask unloadTask = chunkDataUnload == null ? null : chunkDataUnload.task();
|
|
if (unloadTask == null) {
|
|
ret.addProperty("unload_task_priority", "null");
|
|
- ret.addProperty("unload_task_priority_raw", "null");
|
|
+ ret.addProperty("unload_task_suborder", Long.valueOf(0L));
|
|
} else {
|
|
ret.addProperty("unload_task_priority", Objects.toString(unloadTask.getPriority()));
|
|
- ret.addProperty("unload_task_priority_raw", Integer.valueOf(unloadTask.getPriorityInternal()));
|
|
+ ret.addProperty("unload_task_suborder", Long.valueOf(unloadTask.getSubOrder()));
|
|
}
|
|
|
|
ret.addProperty("killed", Boolean.valueOf(this.unloaded));
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/PriorityHolder.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/PriorityHolder.java
|
|
index 261e09454f49d04eb159c984ec695d7c7aa6a3a8..6b468c621b74449a6218391f6477cf63cfc98c7c 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/PriorityHolder.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/PriorityHolder.java
|
|
@@ -1,7 +1,7 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
|
|
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
import java.lang.invoke.VarHandle;
|
|
|
|
public abstract class PriorityHolder {
|
|
@@ -28,8 +28,8 @@ public abstract class PriorityHolder {
|
|
PRIORITY_HANDLE.set((PriorityHolder)this, (int)val);
|
|
}
|
|
|
|
- protected PriorityHolder(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ protected PriorityHolder(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.setPriorityPlain(priority.priority);
|
|
@@ -69,7 +69,7 @@ public abstract class PriorityHolder {
|
|
return;
|
|
}
|
|
|
|
- this.scheduleTask(PrioritisedExecutor.Priority.getPriority(priority));
|
|
+ this.scheduleTask(Priority.getPriority(priority));
|
|
|
|
int failures = 0;
|
|
for (;;) {
|
|
@@ -86,7 +86,7 @@ public abstract class PriorityHolder {
|
|
return;
|
|
}
|
|
|
|
- this.setPriorityScheduled(PrioritisedExecutor.Priority.getPriority(priority));
|
|
+ this.setPriorityScheduled(Priority.getPriority(priority));
|
|
|
|
++failures;
|
|
for (int i = 0; i < failures; ++i) {
|
|
@@ -95,19 +95,19 @@ public abstract class PriorityHolder {
|
|
}
|
|
}
|
|
|
|
- public final PrioritisedExecutor.Priority getPriority() {
|
|
+ public final Priority getPriority() {
|
|
final int ret = this.getPriorityVolatile();
|
|
if ((ret & PRIORITY_EXECUTED) != 0) {
|
|
- return PrioritisedExecutor.Priority.COMPLETING;
|
|
+ return Priority.COMPLETING;
|
|
}
|
|
if ((ret & PRIORITY_SCHEDULED) != 0) {
|
|
return this.getScheduledPriority();
|
|
}
|
|
- return PrioritisedExecutor.Priority.getPriority(ret);
|
|
+ return Priority.getPriority(ret);
|
|
}
|
|
|
|
- public final void lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public final void lowerPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
|
|
@@ -139,8 +139,8 @@ public abstract class PriorityHolder {
|
|
}
|
|
}
|
|
|
|
- public final void setPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public final void setPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
|
|
@@ -168,8 +168,8 @@ public abstract class PriorityHolder {
|
|
}
|
|
}
|
|
|
|
- public final void raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public final void raisePriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
|
|
@@ -203,13 +203,13 @@ public abstract class PriorityHolder {
|
|
|
|
protected abstract void cancelScheduled();
|
|
|
|
- protected abstract PrioritisedExecutor.Priority getScheduledPriority();
|
|
+ protected abstract Priority getScheduledPriority();
|
|
|
|
- protected abstract void scheduleTask(final PrioritisedExecutor.Priority priority);
|
|
+ protected abstract void scheduleTask(final Priority priority);
|
|
|
|
- protected abstract void lowerPriorityScheduled(final PrioritisedExecutor.Priority priority);
|
|
+ protected abstract void lowerPriorityScheduled(final Priority priority);
|
|
|
|
- protected abstract void setPriorityScheduled(final PrioritisedExecutor.Priority priority);
|
|
+ protected abstract void setPriorityScheduled(final Priority priority);
|
|
|
|
- protected abstract void raisePriorityScheduled(final PrioritisedExecutor.Priority priority);
|
|
+ protected abstract void raisePriorityScheduled(final Priority priority);
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java
|
|
index e0b26ccb63596748b80fc6a5e47e373ba811ba8b..5f4b99d8c5453f8ad2e600a57ea4e7dafa2d45f8 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/executor/RadiusAwarePrioritisedExecutor.java
|
|
@@ -1,10 +1,10 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling.executor;
|
|
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
|
|
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
|
-
|
|
import java.util.ArrayList;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
@@ -16,15 +16,36 @@ public class RadiusAwarePrioritisedExecutor {
|
|
return Long.compare(t1.id, t2.id);
|
|
};
|
|
|
|
- private final DependencyTree[] queues = new DependencyTree[PrioritisedExecutor.Priority.TOTAL_SCHEDULABLE_PRIORITIES];
|
|
+ private final PrioritisedExecutor executor;
|
|
+ private final DependencyTree[] queues = new DependencyTree[Priority.TOTAL_SCHEDULABLE_PRIORITIES];
|
|
private static final int NO_TASKS_QUEUED = -1;
|
|
private int selectedQueue = NO_TASKS_QUEUED;
|
|
private boolean canQueueTasks = true;
|
|
|
|
public RadiusAwarePrioritisedExecutor(final PrioritisedExecutor executor, final int maxToSchedule) {
|
|
+ this.executor = executor;
|
|
+
|
|
for (int i = 0; i < this.queues.length; ++i) {
|
|
- this.queues[i] = new DependencyTree(this, executor, maxToSchedule, i);
|
|
+ this.queues[i] = new DependencyTree(this, executor, maxToSchedule);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void setMaxToSchedule(final int maxToSchedule) {
|
|
+ final List<PrioritisedExecutor.PrioritisedTask> tasks;
|
|
+
|
|
+ synchronized (this) {
|
|
+ for (final DependencyTree dependencyTree : this.queues) {
|
|
+ dependencyTree.maxToSchedule = maxToSchedule;
|
|
+ }
|
|
+
|
|
+ if (this.selectedQueue == NO_TASKS_QUEUED || !this.canQueueTasks) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ tasks = this.queues[this.selectedQueue].tryPushTasks();
|
|
}
|
|
+
|
|
+ scheduleTasks(tasks);
|
|
}
|
|
|
|
private boolean canQueueTasks() {
|
|
@@ -56,7 +77,7 @@ public class RadiusAwarePrioritisedExecutor {
|
|
return null;
|
|
}
|
|
|
|
- private List<PrioritisedExecutor.PrioritisedTask> queue(final Task task, final PrioritisedExecutor.Priority priority) {
|
|
+ private List<PrioritisedExecutor.PrioritisedTask> queue(final Task task, final Priority priority) {
|
|
final int priorityId = priority.priority;
|
|
final DependencyTree queue = this.queues[priorityId];
|
|
|
|
@@ -79,7 +100,7 @@ public class RadiusAwarePrioritisedExecutor {
|
|
return null;
|
|
}
|
|
|
|
- if (PrioritisedExecutor.Priority.isHigherPriority(priorityId, this.selectedQueue)) {
|
|
+ if (Priority.isHigherPriority(priorityId, this.selectedQueue)) {
|
|
// prevent the lower priority tree from queueing more tasks
|
|
this.canQueueTasks = false;
|
|
return null;
|
|
@@ -90,7 +111,7 @@ public class RadiusAwarePrioritisedExecutor {
|
|
}
|
|
|
|
public PrioritisedExecutor.PrioritisedTask createTask(final int chunkX, final int chunkZ, final int radius,
|
|
- final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ final Runnable run, final Priority priority) {
|
|
if (radius < 0) {
|
|
throw new IllegalArgumentException("Radius must be > 0: " + radius);
|
|
}
|
|
@@ -99,11 +120,11 @@ public class RadiusAwarePrioritisedExecutor {
|
|
|
|
public PrioritisedExecutor.PrioritisedTask createTask(final int chunkX, final int chunkZ, final int radius,
|
|
final Runnable run) {
|
|
- return this.createTask(chunkX, chunkZ, radius, run, PrioritisedExecutor.Priority.NORMAL);
|
|
+ return this.createTask(chunkX, chunkZ, radius, run, Priority.NORMAL);
|
|
}
|
|
|
|
public PrioritisedExecutor.PrioritisedTask queueTask(final int chunkX, final int chunkZ, final int radius,
|
|
- final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ final Runnable run, final Priority priority) {
|
|
final PrioritisedExecutor.PrioritisedTask ret = this.createTask(chunkX, chunkZ, radius, run, priority);
|
|
|
|
ret.queue();
|
|
@@ -120,15 +141,15 @@ public class RadiusAwarePrioritisedExecutor {
|
|
return ret;
|
|
}
|
|
|
|
- public PrioritisedExecutor.PrioritisedTask createInfiniteRadiusTask(final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ public PrioritisedExecutor.PrioritisedTask createInfiniteRadiusTask(final Runnable run, final Priority priority) {
|
|
return new Task(this, 0, 0, -1, run, priority);
|
|
}
|
|
|
|
public PrioritisedExecutor.PrioritisedTask createInfiniteRadiusTask(final Runnable run) {
|
|
- return this.createInfiniteRadiusTask(run, PrioritisedExecutor.Priority.NORMAL);
|
|
+ return this.createInfiniteRadiusTask(run, Priority.NORMAL);
|
|
}
|
|
|
|
- public PrioritisedExecutor.PrioritisedTask queueInfiniteRadiusTask(final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ public PrioritisedExecutor.PrioritisedTask queueInfiniteRadiusTask(final Runnable run, final Priority priority) {
|
|
final PrioritisedExecutor.PrioritisedTask ret = this.createInfiniteRadiusTask(run, priority);
|
|
|
|
ret.queue();
|
|
@@ -137,20 +158,27 @@ public class RadiusAwarePrioritisedExecutor {
|
|
}
|
|
|
|
public PrioritisedExecutor.PrioritisedTask queueInfiniteRadiusTask(final Runnable run) {
|
|
- final PrioritisedExecutor.PrioritisedTask ret = this.createInfiniteRadiusTask(run, PrioritisedExecutor.Priority.NORMAL);
|
|
+ final PrioritisedExecutor.PrioritisedTask ret = this.createInfiniteRadiusTask(run, Priority.NORMAL);
|
|
|
|
ret.queue();
|
|
|
|
return ret;
|
|
}
|
|
|
|
+ private static void scheduleTasks(final List<PrioritisedExecutor.PrioritisedTask> toSchedule) {
|
|
+ if (toSchedule != null) {
|
|
+ for (int i = 0, len = toSchedule.size(); i < len; ++i) {
|
|
+ toSchedule.get(i).queue();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
// all accesses must be synchronised by the radius aware object
|
|
private static final class DependencyTree {
|
|
|
|
private final RadiusAwarePrioritisedExecutor scheduler;
|
|
private final PrioritisedExecutor executor;
|
|
- private final int maxToSchedule;
|
|
- private final int treeIndex;
|
|
+ private int maxToSchedule;
|
|
|
|
private int currentlyExecuting;
|
|
private long idGenerator;
|
|
@@ -163,11 +191,10 @@ public class RadiusAwarePrioritisedExecutor {
|
|
private final Long2ReferenceOpenHashMap<DependencyNode> nodeByPosition = new Long2ReferenceOpenHashMap<>();
|
|
|
|
public DependencyTree(final RadiusAwarePrioritisedExecutor scheduler, final PrioritisedExecutor executor,
|
|
- final int maxToSchedule, final int treeIndex) {
|
|
+ final int maxToSchedule) {
|
|
this.scheduler = scheduler;
|
|
this.executor = executor;
|
|
this.maxToSchedule = maxToSchedule;
|
|
- this.treeIndex = treeIndex;
|
|
}
|
|
|
|
public boolean hasWaitingTasks() {
|
|
@@ -412,13 +439,13 @@ public class RadiusAwarePrioritisedExecutor {
|
|
private final int chunkZ;
|
|
private final int radius;
|
|
private Runnable run;
|
|
- private PrioritisedExecutor.Priority priority;
|
|
+ private Priority priority;
|
|
|
|
private DependencyNode dependencyNode;
|
|
private PrioritisedExecutor.PrioritisedTask queuedTask;
|
|
|
|
private Task(final RadiusAwarePrioritisedExecutor scheduler, final int chunkX, final int chunkZ, final int radius,
|
|
- final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ final Runnable run, final Priority priority) {
|
|
this.scheduler = scheduler;
|
|
this.chunkX = chunkX;
|
|
this.chunkZ = chunkZ;
|
|
@@ -441,14 +468,6 @@ public class RadiusAwarePrioritisedExecutor {
|
|
run.run();
|
|
}
|
|
|
|
- private static void scheduleTasks(final List<PrioritisedExecutor.PrioritisedTask> toSchedule) {
|
|
- if (toSchedule != null) {
|
|
- for (int i = 0, len = toSchedule.size(); i < len; ++i) {
|
|
- toSchedule.get(i).queue();
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
private void returnNode() {
|
|
final List<PrioritisedExecutor.PrioritisedTask> toSchedule;
|
|
synchronized (this.scheduler) {
|
|
@@ -460,6 +479,11 @@ public class RadiusAwarePrioritisedExecutor {
|
|
scheduleTasks(toSchedule);
|
|
}
|
|
|
|
+ @Override
|
|
+ public PrioritisedExecutor getExecutor() {
|
|
+ return this.scheduler.executor;
|
|
+ }
|
|
+
|
|
@Override
|
|
public void run() {
|
|
final Runnable run = this.run;
|
|
@@ -475,7 +499,7 @@ public class RadiusAwarePrioritisedExecutor {
|
|
public boolean queue() {
|
|
final List<PrioritisedExecutor.PrioritisedTask> toSchedule;
|
|
synchronized (this.scheduler) {
|
|
- if (this.queuedTask != null || this.dependencyNode != null || this.priority == PrioritisedExecutor.Priority.COMPLETING) {
|
|
+ if (this.queuedTask != null || this.dependencyNode != null || this.priority == Priority.COMPLETING) {
|
|
return false;
|
|
}
|
|
|
|
@@ -486,16 +510,23 @@ public class RadiusAwarePrioritisedExecutor {
|
|
return true;
|
|
}
|
|
|
|
+ @Override
|
|
+ public boolean isQueued() {
|
|
+ synchronized (this.scheduler) {
|
|
+ return (this.queuedTask != null || this.dependencyNode != null) && this.priority != Priority.COMPLETING;
|
|
+ }
|
|
+ }
|
|
+
|
|
@Override
|
|
public boolean cancel() {
|
|
final PrioritisedExecutor.PrioritisedTask task;
|
|
synchronized (this.scheduler) {
|
|
if ((task = this.queuedTask) == null) {
|
|
- if (this.priority == PrioritisedExecutor.Priority.COMPLETING) {
|
|
+ if (this.priority == Priority.COMPLETING) {
|
|
return false;
|
|
}
|
|
|
|
- this.priority = PrioritisedExecutor.Priority.COMPLETING;
|
|
+ this.priority = Priority.COMPLETING;
|
|
if (this.dependencyNode != null) {
|
|
this.dependencyNode.purged = true;
|
|
this.dependencyNode = null;
|
|
@@ -519,11 +550,11 @@ public class RadiusAwarePrioritisedExecutor {
|
|
final PrioritisedExecutor.PrioritisedTask task;
|
|
synchronized (this.scheduler) {
|
|
if ((task = this.queuedTask) == null) {
|
|
- if (this.priority == PrioritisedExecutor.Priority.COMPLETING) {
|
|
+ if (this.priority == Priority.COMPLETING) {
|
|
return false;
|
|
}
|
|
|
|
- this.priority = PrioritisedExecutor.Priority.COMPLETING;
|
|
+ this.priority = Priority.COMPLETING;
|
|
if (this.dependencyNode != null) {
|
|
this.dependencyNode.purged = true;
|
|
this.dependencyNode = null;
|
|
@@ -543,7 +574,7 @@ public class RadiusAwarePrioritisedExecutor {
|
|
}
|
|
|
|
@Override
|
|
- public PrioritisedExecutor.Priority getPriority() {
|
|
+ public Priority getPriority() {
|
|
final PrioritisedExecutor.PrioritisedTask task;
|
|
synchronized (this.scheduler) {
|
|
if ((task = this.queuedTask) == null) {
|
|
@@ -555,8 +586,8 @@ public class RadiusAwarePrioritisedExecutor {
|
|
}
|
|
|
|
@Override
|
|
- public boolean setPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public boolean setPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
|
|
@@ -564,7 +595,7 @@ public class RadiusAwarePrioritisedExecutor {
|
|
List<PrioritisedExecutor.PrioritisedTask> toSchedule = null;
|
|
synchronized (this.scheduler) {
|
|
if ((task = this.queuedTask) == null) {
|
|
- if (this.priority == PrioritisedExecutor.Priority.COMPLETING) {
|
|
+ if (this.priority == Priority.COMPLETING) {
|
|
return false;
|
|
}
|
|
|
|
@@ -592,8 +623,8 @@ public class RadiusAwarePrioritisedExecutor {
|
|
}
|
|
|
|
@Override
|
|
- public boolean raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public boolean raisePriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
|
|
@@ -601,7 +632,7 @@ public class RadiusAwarePrioritisedExecutor {
|
|
List<PrioritisedExecutor.PrioritisedTask> toSchedule = null;
|
|
synchronized (this.scheduler) {
|
|
if ((task = this.queuedTask) == null) {
|
|
- if (this.priority == PrioritisedExecutor.Priority.COMPLETING) {
|
|
+ if (this.priority == Priority.COMPLETING) {
|
|
return false;
|
|
}
|
|
|
|
@@ -629,8 +660,8 @@ public class RadiusAwarePrioritisedExecutor {
|
|
}
|
|
|
|
@Override
|
|
- public boolean lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public boolean lowerPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
|
|
@@ -638,7 +669,7 @@ public class RadiusAwarePrioritisedExecutor {
|
|
List<PrioritisedExecutor.PrioritisedTask> toSchedule = null;
|
|
synchronized (this.scheduler) {
|
|
if ((task = this.queuedTask) == null) {
|
|
- if (this.priority == PrioritisedExecutor.Priority.COMPLETING) {
|
|
+ if (this.priority == Priority.COMPLETING) {
|
|
return false;
|
|
}
|
|
|
|
@@ -664,5 +695,35 @@ public class RadiusAwarePrioritisedExecutor {
|
|
|
|
return true;
|
|
}
|
|
+
|
|
+ @Override
|
|
+ public long getSubOrder() {
|
|
+ // TODO implement
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean setSubOrder(final long subOrder) {
|
|
+ // TODO implement
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean raiseSubOrder(final long subOrder) {
|
|
+ // TODO implement
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean lowerSubOrder(final long subOrder) {
|
|
+ // TODO implement
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean setPriorityAndSubOrder(final Priority priority, final long subOrder) {
|
|
+ // TODO implement
|
|
+ return this.setPriority(priority);
|
|
+ }
|
|
}
|
|
-}
|
|
\ No newline at end of file
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java
|
|
index fbdf721e8b4cfe6cef4ee60c53c680cbfc858d88..6ab353b0d2465c3680bb3c8d0852ba0f65c00fd2 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkFullTask.java
|
|
@@ -1,7 +1,9 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task;
|
|
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.ChunkSystemPoiManager;
|
|
@@ -29,7 +31,7 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
|
|
private final PrioritisedExecutor.PrioritisedTask convertToFullTask;
|
|
|
|
public ChunkFullTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final NewChunkHolder chunkHolder, final ChunkAccess fromChunk, final PrioritisedExecutor.Priority priority) {
|
|
+ final NewChunkHolder chunkHolder, final ChunkAccess fromChunk, final Priority priority) {
|
|
super(scheduler, world, chunkX, chunkZ);
|
|
this.chunkHolder = chunkHolder;
|
|
this.fromChunk = fromChunk;
|
|
@@ -43,6 +45,8 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
|
|
|
|
@Override
|
|
public void run() {
|
|
+ final PlatformHooks platformHooks = PlatformHooks.get();
|
|
+
|
|
// See Vanilla ChunkPyramid#LOADING_PYRAMID.FULL for what this function should be doing
|
|
final LevelChunk chunk;
|
|
try {
|
|
@@ -61,7 +65,7 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
|
|
final ServerLevel world = this.world;
|
|
final ProtoChunk protoChunk = (ProtoChunk)this.fromChunk;
|
|
chunk = new LevelChunk(this.world, protoChunk, (final LevelChunk unused) -> {
|
|
- ChunkStatusTasks.postLoadProtoChunk(world, protoChunk.getEntities(), protoChunk.getPos()); // Paper - pass chunk pos
|
|
+ PlatformHooks.get().postLoadProtoChunk(world, protoChunk);
|
|
});
|
|
this.chunkHolder.replaceProtoChunk(new ImposterProtoChunk(chunk, false));
|
|
}
|
|
@@ -71,16 +75,21 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
|
|
final NewChunkHolder chunkHolder = this.chunkHolder;
|
|
|
|
chunk.setFullStatus(chunkHolder::getChunkStatus);
|
|
- chunk.runPostLoad();
|
|
- // Unlike Vanilla, we load the entity chunk here, as we load the NBT in empty status (unlike Vanilla)
|
|
- // This brings entity addition back in line with older versions of the game
|
|
- // Since we load the NBT in the empty status, this will never block for I/O
|
|
- ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager.getOrCreateEntityChunk(this.chunkX, this.chunkZ, false);
|
|
-
|
|
- // we don't need the entitiesInLevel, not sure why it's there
|
|
- chunk.setLoaded(true);
|
|
- chunk.registerAllBlockEntitiesAfterLevelLoad();
|
|
- chunk.registerTickContainerInLevel(this.world);
|
|
+ try {
|
|
+ platformHooks.setCurrentlyLoading(this.chunkHolder.vanillaChunkHolder, chunk);
|
|
+ chunk.runPostLoad();
|
|
+ // Unlike Vanilla, we load the entity chunk here, as we load the NBT in empty status (unlike Vanilla)
|
|
+ // This brings entity addition back in line with older versions of the game
|
|
+ // Since we load the NBT in the empty status, this will never block for I/O
|
|
+ ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager.getOrCreateEntityChunk(this.chunkX, this.chunkZ, false);
|
|
+ chunk.setLoaded(true);
|
|
+ chunk.registerAllBlockEntitiesAfterLevelLoad();
|
|
+ chunk.registerTickContainerInLevel(this.world);
|
|
+ chunk.setUnsavedListener(this.world.getChunkSource().chunkMap.worldGenContext.unsavedListener());
|
|
+ platformHooks.chunkFullStatusComplete(chunk, (ProtoChunk)this.fromChunk);
|
|
+ } finally {
|
|
+ platformHooks.setCurrentlyLoading(this.chunkHolder.vanillaChunkHolder, null);
|
|
+ }
|
|
} catch (final Throwable throwable) {
|
|
this.complete(null, throwable);
|
|
return;
|
|
@@ -112,29 +121,29 @@ public final class ChunkFullTask extends ChunkProgressionTask implements Runnabl
|
|
}
|
|
|
|
@Override
|
|
- public PrioritisedExecutor.Priority getPriority() {
|
|
+ public Priority getPriority() {
|
|
return this.convertToFullTask.getPriority();
|
|
}
|
|
|
|
@Override
|
|
- public void lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public void lowerPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.convertToFullTask.lowerPriority(priority);
|
|
}
|
|
|
|
@Override
|
|
- public void setPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public void setPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.convertToFullTask.setPriority(priority);
|
|
}
|
|
|
|
@Override
|
|
- public void raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public void raisePriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.convertToFullTask.raisePriority(priority);
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLightTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLightTask.java
|
|
index 7c2e6752228fac175c4aa97fa3d817b8a938922f..4538ccfaea83d217ed85eaf16e82393c7f286489 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLightTask.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLightTask.java
|
|
@@ -1,6 +1,6 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task;
|
|
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.PriorityHolder;
|
|
@@ -25,9 +25,9 @@ public final class ChunkLightTask extends ChunkProgressionTask {
|
|
private final LightTaskPriorityHolder priorityHolder;
|
|
|
|
public ChunkLightTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final ChunkAccess chunk, final PrioritisedExecutor.Priority priority) {
|
|
+ final ChunkAccess chunk, final Priority priority) {
|
|
super(scheduler, world, chunkX, chunkZ);
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.priorityHolder = new LightTaskPriorityHolder(priority, this);
|
|
@@ -55,22 +55,22 @@ public final class ChunkLightTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- public PrioritisedExecutor.Priority getPriority() {
|
|
+ public Priority getPriority() {
|
|
return this.priorityHolder.getPriority();
|
|
}
|
|
|
|
@Override
|
|
- public void lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void lowerPriority(final Priority priority) {
|
|
this.priorityHolder.raisePriority(priority);
|
|
}
|
|
|
|
@Override
|
|
- public void setPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void setPriority(final Priority priority) {
|
|
this.priorityHolder.setPriority(priority);
|
|
}
|
|
|
|
@Override
|
|
- public void raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void raisePriority(final Priority priority) {
|
|
this.priorityHolder.raisePriority(priority);
|
|
}
|
|
|
|
@@ -78,7 +78,7 @@ public final class ChunkLightTask extends ChunkProgressionTask {
|
|
|
|
private final ChunkLightTask task;
|
|
|
|
- private LightTaskPriorityHolder(final PrioritisedExecutor.Priority priority, final ChunkLightTask task) {
|
|
+ private LightTaskPriorityHolder(final Priority priority, final ChunkLightTask task) {
|
|
super(priority);
|
|
this.task = task;
|
|
}
|
|
@@ -90,13 +90,13 @@ public final class ChunkLightTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- protected PrioritisedExecutor.Priority getScheduledPriority() {
|
|
+ protected Priority getScheduledPriority() {
|
|
final ChunkLightTask task = this.task;
|
|
return ((StarLightLightingProvider)task.world.getChunkSource().getLightEngine()).starlight$getLightEngine().getServerLightQueue().getPriority(task.chunkX, task.chunkZ);
|
|
}
|
|
|
|
@Override
|
|
- protected void scheduleTask(final PrioritisedExecutor.Priority priority) {
|
|
+ protected void scheduleTask(final Priority priority) {
|
|
final ChunkLightTask task = this.task;
|
|
final StarLightInterface starLightInterface = ((StarLightLightingProvider)task.world.getChunkSource().getLightEngine()).starlight$getLightEngine();
|
|
final StarLightInterface.ServerLightQueue lightQueue = starLightInterface.getServerLightQueue();
|
|
@@ -105,7 +105,7 @@ public final class ChunkLightTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- protected void lowerPriorityScheduled(final PrioritisedExecutor.Priority priority) {
|
|
+ protected void lowerPriorityScheduled(final Priority priority) {
|
|
final ChunkLightTask task = this.task;
|
|
final StarLightInterface starLightInterface = ((StarLightLightingProvider)task.world.getChunkSource().getLightEngine()).starlight$getLightEngine();
|
|
final StarLightInterface.ServerLightQueue lightQueue = starLightInterface.getServerLightQueue();
|
|
@@ -113,7 +113,7 @@ public final class ChunkLightTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- protected void setPriorityScheduled(final PrioritisedExecutor.Priority priority) {
|
|
+ protected void setPriorityScheduled(final Priority priority) {
|
|
final ChunkLightTask task = this.task;
|
|
final StarLightInterface starLightInterface = ((StarLightLightingProvider)task.world.getChunkSource().getLightEngine()).starlight$getLightEngine();
|
|
final StarLightInterface.ServerLightQueue lightQueue = starLightInterface.getServerLightQueue();
|
|
@@ -121,7 +121,7 @@ public final class ChunkLightTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- protected void raisePriorityScheduled(final PrioritisedExecutor.Priority priority) {
|
|
+ protected void raisePriorityScheduled(final Priority priority) {
|
|
final ChunkLightTask task = this.task;
|
|
final StarLightInterface starLightInterface = ((StarLightLightingProvider)task.world.getChunkSource().getLightEngine()).starlight$getLightEngine();
|
|
final StarLightInterface.ServerLightQueue lightQueue = starLightInterface.getServerLightQueue();
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java
|
|
index 1ab93f219246d0b4dcdfd0f685f47c13091425f8..e0a88615a8b6d58191f29b1ff1a26427f0a4c1a6 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkLoadTask.java
|
|
@@ -1,12 +1,13 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task;
|
|
|
|
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.lock.ReentrantAreaLock;
|
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemConverters;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.ChunkSystemFeatures;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
|
@@ -18,7 +19,7 @@ import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.ProtoChunk;
|
|
import net.minecraft.world.level.chunk.UpgradeData;
|
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
-import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
|
+import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
|
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
@@ -41,7 +42,7 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
private final AtomicInteger taskCountToComplete = new AtomicInteger(3); // one for poi, one for entity, and one for chunk data
|
|
|
|
public ChunkLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final NewChunkHolder chunkHolder, final PrioritisedExecutor.Priority priority) {
|
|
+ final NewChunkHolder chunkHolder, final Priority priority) {
|
|
super(scheduler, world, chunkX, chunkZ);
|
|
this.chunkHolder = chunkHolder;
|
|
this.loadTask = new ChunkDataLoadTask(scheduler, world, chunkX, chunkZ, priority);
|
|
@@ -170,12 +171,12 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- public PrioritisedExecutor.Priority getPriority() {
|
|
+ public Priority getPriority() {
|
|
return this.loadTask.getPriority();
|
|
}
|
|
|
|
@Override
|
|
- public void lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void lowerPriority(final Priority priority) {
|
|
final EntityDataLoadTask entityLoad = this.chunkHolder.getEntityDataLoadTask();
|
|
if (entityLoad != null) {
|
|
entityLoad.lowerPriority(priority);
|
|
@@ -191,7 +192,7 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- public void setPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void setPriority(final Priority priority) {
|
|
final EntityDataLoadTask entityLoad = this.chunkHolder.getEntityDataLoadTask();
|
|
if (entityLoad != null) {
|
|
entityLoad.setPriority(priority);
|
|
@@ -207,7 +208,7 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- public void raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void raisePriority(final Priority priority) {
|
|
final EntityDataLoadTask entityLoad = this.chunkHolder.getEntityDataLoadTask();
|
|
if (entityLoad != null) {
|
|
entityLoad.raisePriority(priority);
|
|
@@ -231,8 +232,8 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
protected static final VarHandle COMPLETED_HANDLE = ConcurrentUtil.getVarHandle(CallbackDataLoadTask.class, "completed", boolean.class);
|
|
|
|
protected CallbackDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
|
|
- final int chunkZ, final RegionFileIOThread.RegionFileType type,
|
|
- final PrioritisedExecutor.Priority priority) {
|
|
+ final int chunkZ, final MoonriseRegionFileIO.RegionFileType type,
|
|
+ final Priority priority) {
|
|
super(scheduler, world, chunkX, chunkZ, type, priority);
|
|
}
|
|
|
|
@@ -272,10 +273,13 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
}
|
|
}
|
|
|
|
- private static final class ChunkDataLoadTask extends CallbackDataLoadTask<CompoundTag, ChunkAccess> {
|
|
+
|
|
+ private static record ReadChunk(ProtoChunk protoChunk, SerializableChunkData chunkData) {}
|
|
+
|
|
+ private static final class ChunkDataLoadTask extends CallbackDataLoadTask<ReadChunk, ChunkAccess> {
|
|
private ChunkDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
|
|
- final int chunkZ, final PrioritisedExecutor.Priority priority) {
|
|
- super(scheduler, world, chunkX, chunkZ, RegionFileIOThread.RegionFileType.CHUNK_DATA, priority);
|
|
+ final int chunkZ, final Priority priority) {
|
|
+ super(scheduler, world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, priority);
|
|
}
|
|
|
|
@Override
|
|
@@ -289,40 +293,42 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- protected PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ protected PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final Priority priority) {
|
|
return this.scheduler.loadExecutor.createTask(run, priority);
|
|
}
|
|
|
|
@Override
|
|
- protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final Priority priority) {
|
|
return this.scheduler.createChunkTask(this.chunkX, this.chunkZ, run, priority);
|
|
}
|
|
|
|
@Override
|
|
- protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final CompoundTag data, final Throwable throwable) {
|
|
+ protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final ReadChunk data, final Throwable throwable) {
|
|
if (throwable != null) {
|
|
return new TaskResult<>(null, throwable);
|
|
}
|
|
- if (data == null) {
|
|
+
|
|
+ if (data == null || data.protoChunk() == null) {
|
|
return new TaskResult<>(this.getEmptyChunk(), null);
|
|
}
|
|
|
|
- if (ChunkSystemFeatures.supportsAsyncChunkDeserialization()) {
|
|
- return this.deserialize(data);
|
|
+ if (!PlatformHooks.get().hasMainChunkLoadHook()) {
|
|
+ return new TaskResult<>(data.protoChunk(), null);
|
|
}
|
|
- // need to deserialize on main thread
|
|
+
|
|
+ // need to invoke the callback for loading on the main thread
|
|
return null;
|
|
}
|
|
|
|
private ProtoChunk getEmptyChunk() {
|
|
return new ProtoChunk(
|
|
new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, this.world,
|
|
- this.world.registryAccess().registryOrThrow(Registries.BIOME), (BlendingData)null
|
|
+ this.world.registryAccess().lookupOrThrow(Registries.BIOME), (BlendingData)null
|
|
);
|
|
}
|
|
|
|
@Override
|
|
- protected TaskResult<CompoundTag, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
|
|
+ protected TaskResult<ReadChunk, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
|
|
if (throwable != null) {
|
|
LOGGER.error("Failed to load chunk data for task: " + this.toString() + ", chunk data will be lost", throwable);
|
|
return new TaskResult<>(null, null);
|
|
@@ -334,42 +340,43 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
|
|
try {
|
|
// run converters
|
|
- final CompoundTag converted = this.world.getChunkSource().chunkMap.upgradeChunkTag(data, new net.minecraft.world.level.ChunkPos(this.chunkX, this.chunkZ));
|
|
+ final CompoundTag converted = this.world.getChunkSource().chunkMap.upgradeChunkTag(data);
|
|
|
|
- return new TaskResult<>(converted, null);
|
|
- } catch (final Throwable thr2) {
|
|
- LOGGER.error("Failed to parse chunk data for task: " + this.toString() + ", chunk data will be lost", thr2);
|
|
- return new TaskResult<>(null, null);
|
|
- }
|
|
- }
|
|
+ // unpack the data
|
|
+ final SerializableChunkData chunkData = SerializableChunkData.parse(
|
|
+ this.world, this.world.registryAccess(), converted
|
|
+ );
|
|
|
|
- private TaskResult<ChunkAccess, Throwable> deserialize(final CompoundTag data) {
|
|
- try {
|
|
- final ChunkAccess deserialized = ChunkSerializer.read(
|
|
- this.world, this.world.getPoiManager(), this.world.getChunkSource().chunkMap.storageInfo(), new ChunkPos(this.chunkX, this.chunkZ), data
|
|
+ if (chunkData == null) {
|
|
+ LOGGER.error("Deserialized chunk for task: " + this.toString() + " produced null, chunk data will be lost?");
|
|
+ }
|
|
+
|
|
+ // read into ProtoChunk
|
|
+ final ProtoChunk chunk = chunkData == null ? null : chunkData.read(
|
|
+ this.world, this.world.getPoiManager(), this.world.getChunkSource().chunkMap.storageInfo(),
|
|
+ new ChunkPos(this.chunkX, this.chunkZ)
|
|
);
|
|
- return new TaskResult<>(deserialized, null);
|
|
+
|
|
+ return new TaskResult<>(new ReadChunk(chunk, chunkData), null);
|
|
} catch (final Throwable thr2) {
|
|
LOGGER.error("Failed to parse chunk data for task: " + this.toString() + ", chunk data will be lost", thr2);
|
|
- return new TaskResult<>(this.getEmptyChunk(), null);
|
|
+ return new TaskResult<>(null, null);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
- protected TaskResult<ChunkAccess, Throwable> runOnMain(final CompoundTag data, final Throwable throwable) {
|
|
- // data != null && throwable == null
|
|
- if (ChunkSystemFeatures.supportsAsyncChunkDeserialization()) {
|
|
- throw new UnsupportedOperationException();
|
|
- }
|
|
- return this.deserialize(data);
|
|
+ protected TaskResult<ChunkAccess, Throwable> runOnMain(final ReadChunk data, final Throwable throwable) {
|
|
+ PlatformHooks.get().mainChunkLoad(data.protoChunk(), data.chunkData());
|
|
+
|
|
+ return new TaskResult<>(data.protoChunk(), null);
|
|
}
|
|
}
|
|
|
|
public static final class PoiDataLoadTask extends CallbackDataLoadTask<PoiChunk, PoiChunk> {
|
|
|
|
public PoiDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
|
|
- final int chunkZ, final PrioritisedExecutor.Priority priority) {
|
|
- super(scheduler, world, chunkX, chunkZ, RegionFileIOThread.RegionFileType.POI_DATA, priority);
|
|
+ final int chunkZ, final Priority priority) {
|
|
+ super(scheduler, world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.POI_DATA, priority);
|
|
}
|
|
|
|
@Override
|
|
@@ -383,12 +390,12 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- protected PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ protected PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final Priority priority) {
|
|
return this.scheduler.loadExecutor.createTask(run, priority);
|
|
}
|
|
|
|
@Override
|
|
- protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final Priority priority) {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
@@ -430,8 +437,8 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
public static final class EntityDataLoadTask extends CallbackDataLoadTask<CompoundTag, CompoundTag> {
|
|
|
|
public EntityDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
|
|
- final int chunkZ, final PrioritisedExecutor.Priority priority) {
|
|
- super(scheduler, world, chunkX, chunkZ, RegionFileIOThread.RegionFileType.ENTITY_DATA, priority);
|
|
+ final int chunkZ, final Priority priority) {
|
|
+ super(scheduler, world, chunkX, chunkZ, MoonriseRegionFileIO.RegionFileType.ENTITY_DATA, priority);
|
|
}
|
|
|
|
@Override
|
|
@@ -445,12 +452,12 @@ public final class ChunkLoadTask extends ChunkProgressionTask {
|
|
}
|
|
|
|
@Override
|
|
- protected PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ protected PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final Priority priority) {
|
|
return this.scheduler.loadExecutor.createTask(run, priority);
|
|
}
|
|
|
|
@Override
|
|
- protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
|
|
+ protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final Priority priority) {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java
|
|
index 70e900b0f9c131900bf8b3f3ecbfbd5df5361205..002ee365aa70d8e6a6e6bd5c95988bd17db4395a 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkProgressionTask.java
|
|
@@ -1,8 +1,8 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task;
|
|
|
|
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
@@ -46,15 +46,15 @@ public abstract class ChunkProgressionTask {
|
|
/* May be called multiple times */
|
|
public abstract void cancel();
|
|
|
|
- public abstract PrioritisedExecutor.Priority getPriority();
|
|
+ public abstract Priority getPriority();
|
|
|
|
/* Schedule lock is always held for the priority update calls */
|
|
|
|
- public abstract void lowerPriority(final PrioritisedExecutor.Priority priority);
|
|
+ public abstract void lowerPriority(final Priority priority);
|
|
|
|
- public abstract void setPriority(final PrioritisedExecutor.Priority priority);
|
|
+ public abstract void setPriority(final Priority priority);
|
|
|
|
- public abstract void raisePriority(final PrioritisedExecutor.Priority priority);
|
|
+ public abstract void raisePriority(final Priority priority);
|
|
|
|
public final void onComplete(final BiConsumer<ChunkAccess, Throwable> onComplete) {
|
|
if (!this.waiters.add(onComplete)) {
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java
|
|
index 2c17d5589f15f1155be08be670d29acbe954a8fa..25d8da4773dcee5096053e7e3788bfc224d705a7 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/ChunkUpgradeGenericStatusTask.java
|
|
@@ -1,7 +1,8 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task;
|
|
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkStatus;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
|
@@ -36,9 +37,9 @@ public final class ChunkUpgradeGenericStatusTask extends ChunkProgressionTask im
|
|
|
|
public ChunkUpgradeGenericStatusTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
|
|
final int chunkZ, final ChunkAccess chunk, final StaticCache2D<GenerationChunkHolder> neighbours,
|
|
- final ChunkStatus toStatus, final PrioritisedExecutor.Priority priority) {
|
|
+ final ChunkStatus toStatus, final Priority priority) {
|
|
super(scheduler, world, chunkX, chunkZ);
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.fromChunk = chunk;
|
|
@@ -187,29 +188,29 @@ public final class ChunkUpgradeGenericStatusTask extends ChunkProgressionTask im
|
|
}
|
|
|
|
@Override
|
|
- public PrioritisedExecutor.Priority getPriority() {
|
|
+ public Priority getPriority() {
|
|
return this.generateTask.getPriority();
|
|
}
|
|
|
|
@Override
|
|
- public void lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public void lowerPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.generateTask.lowerPriority(priority);
|
|
}
|
|
|
|
@Override
|
|
- public void setPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public void setPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.generateTask.setPriority(priority);
|
|
}
|
|
|
|
@Override
|
|
- public void raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public void raisePriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.generateTask.raisePriority(priority);
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java
|
|
index 7a65d351b448873c6f2c145c975c92be314b876c..bdcd1879457bafcca4e76523aac0555968f37c0b 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/task/GenericDataLoadTask.java
|
|
@@ -1,12 +1,13 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.scheduling.task;
|
|
|
|
+import ca.spottedleaf.concurrentutil.completable.CallbackCompletable;
|
|
import ca.spottedleaf.concurrentutil.completable.Completable;
|
|
import ca.spottedleaf.concurrentutil.executor.Cancellable;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.DelayedPrioritisedTask;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
-import ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
|
@@ -47,11 +48,11 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
protected final ServerLevel world;
|
|
protected final int chunkX;
|
|
protected final int chunkZ;
|
|
- protected final RegionFileIOThread.RegionFileType type;
|
|
+ protected final MoonriseRegionFileIO.RegionFileType type;
|
|
|
|
public GenericDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
|
|
- final int chunkZ, final RegionFileIOThread.RegionFileType type,
|
|
- final PrioritisedExecutor.Priority priority) {
|
|
+ final int chunkZ, final MoonriseRegionFileIO.RegionFileType type,
|
|
+ final Priority priority) {
|
|
this.scheduler = scheduler;
|
|
this.world = world;
|
|
this.chunkX = chunkX;
|
|
@@ -89,9 +90,9 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
|
|
protected abstract boolean hasOnMain();
|
|
|
|
- protected abstract PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final PrioritisedExecutor.Priority priority);
|
|
+ protected abstract PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final Priority priority);
|
|
|
|
- protected abstract PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final PrioritisedExecutor.Priority priority);
|
|
+ protected abstract PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final Priority priority);
|
|
|
|
protected abstract TaskResult<OnMain, Throwable> runOffMain(final CompoundTag data, final Throwable throwable);
|
|
|
|
@@ -108,7 +109,7 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
", type: " + this.type.toString() + "}";
|
|
}
|
|
|
|
- public PrioritisedExecutor.Priority getPriority() {
|
|
+ public Priority getPriority() {
|
|
if (this.processOnMain != null) {
|
|
return this.processOnMain.getPriority();
|
|
} else {
|
|
@@ -116,7 +117,7 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
}
|
|
}
|
|
|
|
- public void lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void lowerPriority(final Priority priority) {
|
|
// can't lower I/O tasks, we don't know what they affect
|
|
if (this.processOffMain != null) {
|
|
this.processOffMain.lowerPriority(priority);
|
|
@@ -126,7 +127,7 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
}
|
|
}
|
|
|
|
- public void setPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void setPriority(final Priority priority) {
|
|
// can't lower I/O tasks, we don't know what they affect
|
|
this.loadDataFromDiskTask.raisePriority(priority);
|
|
if (this.processOffMain != null) {
|
|
@@ -137,7 +138,7 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
}
|
|
}
|
|
|
|
- public void raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void raisePriority(final Priority priority) {
|
|
// can't lower I/O tasks, we don't know what they affect
|
|
this.loadDataFromDiskTask.raisePriority(priority);
|
|
if (this.processOffMain != null) {
|
|
@@ -382,10 +383,10 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
private final int chunkX;
|
|
private final int chunkZ;
|
|
|
|
- private final RegionFileIOThread.RegionFileType type;
|
|
+ private final MoonriseRegionFileIO.RegionFileType type;
|
|
private Cancellable dataLoadTask;
|
|
private Cancellable dataUnloadCancellable;
|
|
- private DelayedPrioritisedTask dataUnloadTask;
|
|
+ private PrioritisedExecutor.PrioritisedTask dataUnloadTask;
|
|
|
|
private final BiConsumer<CompoundTag, Throwable> onComplete;
|
|
private final AtomicBoolean scheduled = new AtomicBoolean();
|
|
@@ -393,10 +394,10 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
// onComplete should be caller sensitive, it may complete synchronously with schedule() - which does
|
|
// hold a priority lock.
|
|
public LoadDataFromDiskTask(final ServerLevel world, final int chunkX, final int chunkZ,
|
|
- final RegionFileIOThread.RegionFileType type,
|
|
+ final MoonriseRegionFileIO.RegionFileType type,
|
|
final BiConsumer<CompoundTag, Throwable> onComplete,
|
|
- final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
this.world = world;
|
|
@@ -426,8 +427,8 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
return (this.getPriorityVolatile() & PRIORITY_EXECUTED) != 0;
|
|
}
|
|
|
|
- public void lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public void lowerPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
|
|
@@ -439,7 +440,7 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
}
|
|
|
|
if ((curr & PRIORITY_LOAD_SCHEDULED) != 0) {
|
|
- RegionFileIOThread.lowerPriority(this.world, this.chunkX, this.chunkZ, this.type, priority);
|
|
+ MoonriseRegionFileIO.lowerPriority(this.world, this.chunkX, this.chunkZ, this.type, priority);
|
|
return;
|
|
}
|
|
|
|
@@ -467,8 +468,8 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
}
|
|
}
|
|
|
|
- public void setPriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public void setPriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
|
|
@@ -480,7 +481,7 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
}
|
|
|
|
if ((curr & PRIORITY_LOAD_SCHEDULED) != 0) {
|
|
- RegionFileIOThread.setPriority(this.world, this.chunkX, this.chunkZ, this.type, priority);
|
|
+ MoonriseRegionFileIO.setPriority(this.world, this.chunkX, this.chunkZ, this.type, priority);
|
|
return;
|
|
}
|
|
|
|
@@ -504,8 +505,8 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
}
|
|
}
|
|
|
|
- public void raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
- if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
|
|
+ public void raisePriority(final Priority priority) {
|
|
+ if (!Priority.isValidPriority(priority)) {
|
|
throw new IllegalArgumentException("Invalid priority " + priority);
|
|
}
|
|
|
|
@@ -517,7 +518,7 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
}
|
|
|
|
if ((curr & PRIORITY_LOAD_SCHEDULED) != 0) {
|
|
- RegionFileIOThread.raisePriority(this.world, this.chunkX, this.chunkZ, this.type, priority);
|
|
+ MoonriseRegionFileIO.raisePriority(this.world, this.chunkX, this.chunkZ, this.type, priority);
|
|
return;
|
|
}
|
|
|
|
@@ -583,7 +584,7 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
} // else: cancelled
|
|
};
|
|
|
|
- final PrioritisedExecutor.Priority initialPriority = PrioritisedExecutor.Priority.getPriority(priority);
|
|
+ final Priority initialPriority = Priority.getPriority(priority);
|
|
boolean scheduledUnload = false;
|
|
|
|
final NewChunkHolder holder = ((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(this.chunkX, this.chunkZ);
|
|
@@ -593,13 +594,13 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
consumer.accept(data, null);
|
|
} else {
|
|
// need to schedule task
|
|
- LoadDataFromDiskTask.this.schedule(false, consumer, PrioritisedExecutor.Priority.getPriority(LoadDataFromDiskTask.this.getPriorityVolatile() & ~PRIORITY_FLAGS));
|
|
+ LoadDataFromDiskTask.this.schedule(false, consumer, Priority.getPriority(LoadDataFromDiskTask.this.getPriorityVolatile() & ~PRIORITY_FLAGS));
|
|
}
|
|
};
|
|
Cancellable unloadCancellable = null;
|
|
CompoundTag syncComplete = null;
|
|
final NewChunkHolder.UnloadTask unloadTask = holder.getUnloadTask(this.type); // can be null if no task exists
|
|
- final Completable<CompoundTag> unloadCompletable = unloadTask == null ? null : unloadTask.completable();
|
|
+ final CallbackCompletable<CompoundTag> unloadCompletable = unloadTask == null ? null : unloadTask.completable();
|
|
if (unloadCompletable != null) {
|
|
unloadCancellable = unloadCompletable.addAsynchronousWaiter(unloadConsumer);
|
|
if (unloadCancellable == null) {
|
|
@@ -622,7 +623,7 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
this.schedule(scheduledUnload, consumer, initialPriority);
|
|
}
|
|
|
|
- private void schedule(final boolean scheduledUnload, final BiConsumer<CompoundTag, Throwable> consumer, final PrioritisedExecutor.Priority initialPriority) {
|
|
+ private void schedule(final boolean scheduledUnload, final BiConsumer<CompoundTag, Throwable> consumer, final Priority initialPriority) {
|
|
int priority = this.getPriorityVolatile();
|
|
|
|
if ((priority & PRIORITY_EXECUTED) != 0) {
|
|
@@ -631,9 +632,9 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
}
|
|
|
|
if (!scheduledUnload) {
|
|
- this.dataLoadTask = RegionFileIOThread.loadDataAsync(
|
|
+ this.dataLoadTask = MoonriseRegionFileIO.loadDataAsync(
|
|
this.world, this.chunkX, this.chunkZ, this.type, consumer,
|
|
- initialPriority.isHigherPriority(PrioritisedExecutor.Priority.NORMAL), initialPriority
|
|
+ initialPriority.isHigherPriority(Priority.NORMAL), initialPriority
|
|
);
|
|
}
|
|
|
|
@@ -657,10 +658,10 @@ public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
|
|
|
|
if (scheduledUnload) {
|
|
if (this.dataUnloadTask != null) {
|
|
- this.dataUnloadTask.setPriority(PrioritisedExecutor.Priority.getPriority(priority & ~PRIORITY_FLAGS));
|
|
+ this.dataUnloadTask.setPriority(Priority.getPriority(priority & ~PRIORITY_FLAGS));
|
|
}
|
|
} else {
|
|
- RegionFileIOThread.setPriority(this.world, this.chunkX, this.chunkZ, this.type, PrioritisedExecutor.Priority.getPriority(priority & ~PRIORITY_FLAGS));
|
|
+ MoonriseRegionFileIO.setPriority(this.world, this.chunkX, this.chunkZ, this.type, Priority.getPriority(priority & ~PRIORITY_FLAGS));
|
|
}
|
|
|
|
++failures;
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..51c126735ace8fdde89ad97b5cab62f244212db0
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java
|
|
@@ -0,0 +1,12 @@
|
|
+package ca.spottedleaf.moonrise.patches.chunk_system.storage;
|
|
+
|
|
+import net.minecraft.world.level.chunk.storage.RegionFile;
|
|
+import java.io.IOException;
|
|
+
|
|
+public interface ChunkSystemChunkBuffer {
|
|
+ public boolean moonrise$getWriteOnClose();
|
|
+
|
|
+ public void moonrise$setWriteOnClose(final boolean value);
|
|
+
|
|
+ public void moonrise$write(final RegionFile regionFile) throws IOException;
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemRegionFile.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemRegionFile.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..3bd1b59250dbab15097a64d515999b278636795a
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemRegionFile.java
|
|
@@ -0,0 +1,12 @@
|
|
+package ca.spottedleaf.moonrise.patches.chunk_system.storage;
|
|
+
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO;
|
|
+import net.minecraft.nbt.CompoundTag;
|
|
+import net.minecraft.world.level.ChunkPos;
|
|
+import java.io.IOException;
|
|
+
|
|
+public interface ChunkSystemRegionFile {
|
|
+
|
|
+ public MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite(final CompoundTag data, final ChunkPos pos) throws IOException;
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java
|
|
index 3a9a564edfdb99e006e4816cb8821bd1e9ecff43..93fd23027c00cef76562098306737272fda1350a 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/util/ParallelSearchRadiusIteration.java
|
|
@@ -1,6 +1,7 @@
|
|
package ca.spottedleaf.moonrise.patches.chunk_system.util;
|
|
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
+import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
|
|
import it.unimi.dsi.fastutil.HashCommon;
|
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
import it.unimi.dsi.fastutil.longs.LongIterator;
|
|
@@ -13,7 +14,7 @@ public final class ParallelSearchRadiusIteration {
|
|
|
|
// expected that this list returns for a given radius, the set of chunks ordered
|
|
// by manhattan distance
|
|
- private static final long[][] SEARCH_RADIUS_ITERATION_LIST = new long[64+2+1][];
|
|
+ private static final long[][] SEARCH_RADIUS_ITERATION_LIST = new long[MoonriseConstants.MAX_VIEW_DISTANCE+2+1][];
|
|
static {
|
|
for (int i = 0; i < SEARCH_RADIUS_ITERATION_LIST.length; ++i) {
|
|
// a BFS around -x, -z, +x, +z will give increasing manhatten distance
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/util/stream/ExternalChunkStreamMarker.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/util/stream/ExternalChunkStreamMarker.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..7ef3dcca89ed7578c6c0f5565131889110063056
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/util/stream/ExternalChunkStreamMarker.java
|
|
@@ -0,0 +1,37 @@
|
|
+package ca.spottedleaf.moonrise.patches.chunk_system.util.stream;
|
|
+
|
|
+import java.io.DataInputStream;
|
|
+import java.io.FilterInputStream;
|
|
+import java.io.InputStream;
|
|
+import java.lang.reflect.Field;
|
|
+
|
|
+/**
|
|
+ * Used to mark chunk data streams that are on external files
|
|
+ */
|
|
+public class ExternalChunkStreamMarker extends DataInputStream {
|
|
+
|
|
+ private static final Field IN_FIELD;
|
|
+ static {
|
|
+ Field field;
|
|
+ try {
|
|
+ field = FilterInputStream.class.getDeclaredField("in");
|
|
+ field.setAccessible(true);
|
|
+ } catch (final Throwable throwable) {
|
|
+ field = null;
|
|
+ }
|
|
+
|
|
+ IN_FIELD = field;
|
|
+ }
|
|
+
|
|
+ private static InputStream getWrapped(final FilterInputStream in) {
|
|
+ try {
|
|
+ return (InputStream)IN_FIELD.get(in);
|
|
+ } catch (final Throwable throwable) {
|
|
+ return in;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public ExternalChunkStreamMarker(final DataInputStream in) {
|
|
+ super(getWrapped(in));
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
|
index 748ab4d637ce463272bae4fdbab6842a27385126..3abd4ad6379c383c3a31931255292b42d9435694 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
|
@@ -1,15 +1,58 @@
|
|
package ca.spottedleaf.moonrise.patches.collisions;
|
|
|
|
+import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter;
|
|
+import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState;
|
|
+import ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity;
|
|
+import ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData;
|
|
+import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionDiscreteVoxelShape;
|
|
+import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape;
|
|
+import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection;
|
|
+import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
|
+import it.unimi.dsi.fastutil.doubles.DoubleList;
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.core.Direction;
|
|
+import net.minecraft.util.Mth;
|
|
+import net.minecraft.world.entity.Entity;
|
|
+import net.minecraft.world.item.Item;
|
|
+import net.minecraft.world.level.Level;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.border.WorldBorder;
|
|
+import net.minecraft.world.level.chunk.ChunkAccess;
|
|
+import net.minecraft.world.level.chunk.ChunkSource;
|
|
+import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
+import net.minecraft.world.level.chunk.PalettedContainer;
|
|
+import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
+import net.minecraft.world.level.material.FluidState;
|
|
+import net.minecraft.world.phys.AABB;
|
|
+import net.minecraft.world.phys.Vec3;
|
|
+import net.minecraft.world.phys.shapes.ArrayVoxelShape;
|
|
+import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape;
|
|
+import net.minecraft.world.phys.shapes.BooleanOp;
|
|
+import net.minecraft.world.phys.shapes.CollisionContext;
|
|
+import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
|
|
+import net.minecraft.world.phys.shapes.EntityCollisionContext;
|
|
+import net.minecraft.world.phys.shapes.OffsetDoubleList;
|
|
+import net.minecraft.world.phys.shapes.Shapes;
|
|
+import net.minecraft.world.phys.shapes.SliceShape;
|
|
+import net.minecraft.world.phys.shapes.VoxelShape;
|
|
+import java.util.Arrays;
|
|
+import java.util.List;
|
|
+import java.util.Objects;
|
|
+import java.util.function.BiPredicate;
|
|
+import java.util.function.Predicate;
|
|
+
|
|
public final class CollisionUtil {
|
|
|
|
public static final double COLLISION_EPSILON = 1.0E-7;
|
|
- public static final it.unimi.dsi.fastutil.doubles.DoubleArrayList ZERO_ONE = it.unimi.dsi.fastutil.doubles.DoubleArrayList.wrap(new double[] { 0.0, 1.0 });
|
|
+ public static final DoubleArrayList ZERO_ONE = DoubleArrayList.wrap(new double[] { 0.0, 1.0 });
|
|
|
|
public static boolean isSpecialCollidingBlock(final net.minecraft.world.level.block.state.BlockBehaviour.BlockStateBase block) {
|
|
- return block.hasLargeCollisionShape() || block.getBlock() == net.minecraft.world.level.block.Blocks.MOVING_PISTON;
|
|
+ return block.hasLargeCollisionShape() || block.getBlock() == Blocks.MOVING_PISTON;
|
|
}
|
|
|
|
- public static boolean isEmpty(final net.minecraft.world.phys.AABB aabb) {
|
|
+ public static boolean isEmpty(final AABB aabb) {
|
|
return (aabb.maxX - aabb.minX) < COLLISION_EPSILON || (aabb.maxY - aabb.minY) < COLLISION_EPSILON || (aabb.maxZ - aabb.minZ) < COLLISION_EPSILON;
|
|
}
|
|
|
|
@@ -18,11 +61,11 @@ public final class CollisionUtil {
|
|
return (maxX - minX) < COLLISION_EPSILON || (maxY - minY) < COLLISION_EPSILON || (maxZ - minZ) < COLLISION_EPSILON;
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB getBoxForChunk(final int chunkX, final int chunkZ) {
|
|
+ public static AABB getBoxForChunk(final int chunkX, final int chunkZ) {
|
|
double x = (double)(chunkX << 4);
|
|
double z = (double)(chunkZ << 4);
|
|
// use a bounding box bigger than the chunk to prevent entities from entering it on move
|
|
- return new net.minecraft.world.phys.AABB(x - 3*COLLISION_EPSILON, Double.NEGATIVE_INFINITY, z - 3*COLLISION_EPSILON,
|
|
+ return new AABB(x - 3*COLLISION_EPSILON, Double.NEGATIVE_INFINITY, z - 3*COLLISION_EPSILON,
|
|
x + (16.0 + 3*COLLISION_EPSILON), Double.POSITIVE_INFINITY, z + (16.0 + 3*COLLISION_EPSILON));
|
|
}
|
|
|
|
@@ -43,21 +86,21 @@ public final class CollisionUtil {
|
|
(minZ1 - maxZ2) < -COLLISION_EPSILON && (maxZ1 - minZ2) > COLLISION_EPSILON;
|
|
}
|
|
|
|
- public static boolean voxelShapeIntersect(final net.minecraft.world.phys.AABB box, final double minX, final double minY, final double minZ,
|
|
+ public static boolean voxelShapeIntersect(final AABB box, final double minX, final double minY, final double minZ,
|
|
final double maxX, final double maxY, final double maxZ) {
|
|
return (box.minX - maxX) < -COLLISION_EPSILON && (box.maxX - minX) > COLLISION_EPSILON &&
|
|
(box.minY - maxY) < -COLLISION_EPSILON && (box.maxY - minY) > COLLISION_EPSILON &&
|
|
(box.minZ - maxZ) < -COLLISION_EPSILON && (box.maxZ - minZ) > COLLISION_EPSILON;
|
|
}
|
|
|
|
- public static boolean voxelShapeIntersect(final net.minecraft.world.phys.AABB box1, final net.minecraft.world.phys.AABB box2) {
|
|
+ public static boolean voxelShapeIntersect(final AABB box1, final AABB box2) {
|
|
return (box1.minX - box2.maxX) < -COLLISION_EPSILON && (box1.maxX - box2.minX) > COLLISION_EPSILON &&
|
|
(box1.minY - box2.maxY) < -COLLISION_EPSILON && (box1.maxY - box2.minY) > COLLISION_EPSILON &&
|
|
(box1.minZ - box2.maxZ) < -COLLISION_EPSILON && (box1.maxZ - box2.minZ) > COLLISION_EPSILON;
|
|
}
|
|
|
|
// assume !isEmpty(target) && abs(source_move) >= COLLISION_EPSILON
|
|
- public static double collideX(final net.minecraft.world.phys.AABB target, final net.minecraft.world.phys.AABB source, final double source_move) {
|
|
+ public static double collideX(final AABB target, final AABB source, final double source_move) {
|
|
if ((source.minY - target.maxY) < -COLLISION_EPSILON && (source.maxY - target.minY) > COLLISION_EPSILON &&
|
|
(source.minZ - target.maxZ) < -COLLISION_EPSILON && (source.maxZ - target.minZ) > COLLISION_EPSILON) {
|
|
if (source_move >= 0.0) {
|
|
@@ -78,7 +121,7 @@ public final class CollisionUtil {
|
|
}
|
|
|
|
// assume !isEmpty(target) && abs(source_move) >= COLLISION_EPSILON
|
|
- public static double collideY(final net.minecraft.world.phys.AABB target, final net.minecraft.world.phys.AABB source, final double source_move) {
|
|
+ public static double collideY(final AABB target, final AABB source, final double source_move) {
|
|
if ((source.minX - target.maxX) < -COLLISION_EPSILON && (source.maxX - target.minX) > COLLISION_EPSILON &&
|
|
(source.minZ - target.maxZ) < -COLLISION_EPSILON && (source.maxZ - target.minZ) > COLLISION_EPSILON) {
|
|
if (source_move >= 0.0) {
|
|
@@ -99,7 +142,7 @@ public final class CollisionUtil {
|
|
}
|
|
|
|
// assume !isEmpty(target) && abs(source_move) >= COLLISION_EPSILON
|
|
- public static double collideZ(final net.minecraft.world.phys.AABB target, final net.minecraft.world.phys.AABB source, final double source_move) {
|
|
+ public static double collideZ(final AABB target, final AABB source, final double source_move) {
|
|
if ((source.minX - target.maxX) < -COLLISION_EPSILON && (source.maxX - target.minX) > COLLISION_EPSILON &&
|
|
(source.minY - target.maxY) < -COLLISION_EPSILON && (source.maxY - target.minY) > COLLISION_EPSILON) {
|
|
if (source_move >= 0.0) {
|
|
@@ -121,7 +164,8 @@ public final class CollisionUtil {
|
|
|
|
// startIndex and endIndex inclusive
|
|
// assumes indices are in range of array
|
|
- private static int findFloor(final double[] values, final double value, int startIndex, int endIndex) {
|
|
+ public static int findFloor(final double[] values, final double value, int startIndex, int endIndex) {
|
|
+ Objects.checkFromToIndex(startIndex, endIndex + 1, values.length);
|
|
do {
|
|
final int middle = (startIndex + endIndex) >>> 1;
|
|
final double middleVal = values[middle];
|
|
@@ -136,7 +180,217 @@ public final class CollisionUtil {
|
|
return startIndex - 1;
|
|
}
|
|
|
|
- public static boolean voxelShapeIntersectNoEmpty(final net.minecraft.world.phys.shapes.VoxelShape voxel, final net.minecraft.world.phys.AABB aabb) {
|
|
+ private static VoxelShape sliceShapeVanilla(final VoxelShape src, final Direction.Axis axis,
|
|
+ final int index) {
|
|
+ return new SliceShape(src, axis, index);
|
|
+ }
|
|
+
|
|
+ private static DoubleList offsetList(final double[] src, final double by) {
|
|
+ final DoubleArrayList wrap = DoubleArrayList.wrap(src);
|
|
+ if (by == 0.0) {
|
|
+ return wrap;
|
|
+ }
|
|
+ return new OffsetDoubleList(wrap, by);
|
|
+ }
|
|
+
|
|
+ private static VoxelShape sliceShapeOptimised(final VoxelShape src, final Direction.Axis axis,
|
|
+ final int index) {
|
|
+ // assume index in range
|
|
+ final double off_x = ((CollisionVoxelShape)src).moonrise$offsetX();
|
|
+ final double off_y = ((CollisionVoxelShape)src).moonrise$offsetY();
|
|
+ final double off_z = ((CollisionVoxelShape)src).moonrise$offsetZ();
|
|
+
|
|
+ final double[] coords_x = ((CollisionVoxelShape)src).moonrise$rootCoordinatesX();
|
|
+ final double[] coords_y = ((CollisionVoxelShape)src).moonrise$rootCoordinatesY();
|
|
+ final double[] coords_z = ((CollisionVoxelShape)src).moonrise$rootCoordinatesZ();
|
|
+
|
|
+ final CachedShapeData cached_shape_data = ((CollisionVoxelShape)src).moonrise$getCachedVoxelData();
|
|
+
|
|
+ // note: size = coords.length - 1
|
|
+ final int size_x = cached_shape_data.sizeX();
|
|
+ final int size_y = cached_shape_data.sizeY();
|
|
+ final int size_z = cached_shape_data.sizeZ();
|
|
+
|
|
+ final long[] bitset = cached_shape_data.voxelSet();
|
|
+
|
|
+ final DoubleList list_x;
|
|
+ final DoubleList list_y;
|
|
+ final DoubleList list_z;
|
|
+ final int shape_sx;
|
|
+ final int shape_ex;
|
|
+ final int shape_sy;
|
|
+ final int shape_ey;
|
|
+ final int shape_sz;
|
|
+ final int shape_ez;
|
|
+
|
|
+ switch (axis) {
|
|
+ case X: {
|
|
+ // validate index
|
|
+ if (index < 0 || index >= size_x) {
|
|
+ return Shapes.empty();
|
|
+ }
|
|
+
|
|
+ // test if input is already "sliced"
|
|
+ if (coords_x.length == 2 && (coords_x[0] + off_x) == 0.0 && (coords_x[1] + off_x) == 1.0) {
|
|
+ return src;
|
|
+ }
|
|
+
|
|
+ // test if result would be full box
|
|
+ if (coords_y.length == 2 && coords_z.length == 2 &&
|
|
+ (coords_y[0] + off_y) == 0.0 && (coords_y[1] + off_y) == 1.0 &&
|
|
+ (coords_z[0] + off_z) == 0.0 && (coords_z[1] + off_z) == 1.0) {
|
|
+ // note: size_y == size_z == 1
|
|
+ final int bitIdx = 0 + 0*size_z + index*(size_z*size_y);
|
|
+ return (bitset[bitIdx >>> 6] & (1L << bitIdx)) == 0L ? Shapes.empty() : Shapes.block();
|
|
+ }
|
|
+
|
|
+ list_x = ZERO_ONE;
|
|
+ list_y = offsetList(coords_y, off_y);
|
|
+ list_z = offsetList(coords_z, off_z);
|
|
+ shape_sx = index;
|
|
+ shape_ex = index + 1;
|
|
+ shape_sy = 0;
|
|
+ shape_ey = size_y;
|
|
+ shape_sz = 0;
|
|
+ shape_ez = size_z;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case Y: {
|
|
+ // validate index
|
|
+ if (index < 0 || index >= size_y) {
|
|
+ return Shapes.empty();
|
|
+ }
|
|
+
|
|
+ // test if input is already "sliced"
|
|
+ if (coords_y.length == 2 && (coords_y[0] + off_y) == 0.0 && (coords_y[1] + off_y) == 1.0) {
|
|
+ return src;
|
|
+ }
|
|
+
|
|
+ // test if result would be full box
|
|
+ if (coords_x.length == 2 && coords_z.length == 2 &&
|
|
+ (coords_x[0] + off_x) == 0.0 && (coords_x[1] + off_x) == 1.0 &&
|
|
+ (coords_z[0] + off_z) == 0.0 && (coords_z[1] + off_z) == 1.0) {
|
|
+ // note: size_x == size_z == 1
|
|
+ final int bitIdx = 0 + index*size_z + 0*(size_z*size_y);
|
|
+ return (bitset[bitIdx >>> 6] & (1L << bitIdx)) == 0L ? Shapes.empty() : Shapes.block();
|
|
+ }
|
|
+
|
|
+ list_x = offsetList(coords_x, off_x);
|
|
+ list_y = ZERO_ONE;
|
|
+ list_z = offsetList(coords_z, off_z);
|
|
+ shape_sx = 0;
|
|
+ shape_ex = size_x;
|
|
+ shape_sy = index;
|
|
+ shape_ey = index + 1;
|
|
+ shape_sz = 0;
|
|
+ shape_ez = size_z;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case Z: {
|
|
+ // validate index
|
|
+ if (index < 0 || index >= size_z) {
|
|
+ return Shapes.empty();
|
|
+ }
|
|
+
|
|
+ // test if input is already "sliced"
|
|
+ if (coords_z.length == 2 && (coords_z[0] + off_z) == 0.0 && (coords_z[1] + off_z) == 1.0) {
|
|
+ return src;
|
|
+ }
|
|
+
|
|
+ // test if result would be full box
|
|
+ if (coords_x.length == 2 && coords_y.length == 2 &&
|
|
+ (coords_x[0] + off_x) == 0.0 && (coords_x[1] + off_x) == 1.0 &&
|
|
+ (coords_y[0] + off_y) == 0.0 && (coords_y[1] + off_y) == 1.0) {
|
|
+ // note: size_x == size_y == 1
|
|
+ final int bitIdx = index + 0*size_z + 0*(size_z*size_y);
|
|
+ return (bitset[bitIdx >>> 6] & (1L << bitIdx)) == 0L ? Shapes.empty() : Shapes.block();
|
|
+ }
|
|
+
|
|
+ list_x = offsetList(coords_x, off_x);
|
|
+ list_y = offsetList(coords_y, off_y);
|
|
+ list_z = ZERO_ONE;
|
|
+ shape_sx = 0;
|
|
+ shape_ex = size_x;
|
|
+ shape_sy = 0;
|
|
+ shape_ey = size_y;
|
|
+ shape_sz = index;
|
|
+ shape_ez = index + 1;
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ default: {
|
|
+ throw new IllegalStateException("Unknown axis: " + axis);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final int local_len_x = shape_ex - shape_sx;
|
|
+ final int local_len_y = shape_ey - shape_sy;
|
|
+ final int local_len_z = shape_ez - shape_sz;
|
|
+
|
|
+ final BitSetDiscreteVoxelShape shape = new BitSetDiscreteVoxelShape(local_len_x, local_len_y, local_len_z);
|
|
+
|
|
+ final int bitset_mul_x = size_z*size_y;
|
|
+ final int idx_off = shape_sz + shape_sy*size_z + shape_sx*bitset_mul_x;
|
|
+ final int shape_mul_x = local_len_y*local_len_z;
|
|
+ for (int x = 0; x < local_len_x; ++x) {
|
|
+ boolean setX = false;
|
|
+ for (int y = 0; y < local_len_y; ++y) {
|
|
+ boolean setY = false;
|
|
+ for (int z = 0; z < local_len_z; ++z) {
|
|
+ final int unslicedIdx = idx_off + z + y*size_z + x*bitset_mul_x;
|
|
+ if ((bitset[unslicedIdx >>> 6] & (1L << unslicedIdx)) == 0L) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ setY = true;
|
|
+ setX = true;
|
|
+ shape.zMin = Math.min(shape.zMin, z);
|
|
+ shape.zMax = Math.max(shape.zMax, z + 1);
|
|
+
|
|
+ shape.storage.set(
|
|
+ z + y*local_len_z + x*shape_mul_x
|
|
+ );
|
|
+ }
|
|
+
|
|
+ if (setY) {
|
|
+ shape.yMin = Math.min(shape.yMin, y);
|
|
+ shape.yMax = Math.max(shape.yMax, y + 1);
|
|
+ }
|
|
+ }
|
|
+ if (setX) {
|
|
+ shape.xMin = Math.min(shape.xMin, x);
|
|
+ shape.xMax = Math.max(shape.xMax, x + 1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return shape.isEmpty() ? Shapes.empty() : new ArrayVoxelShape(
|
|
+ shape, list_x, list_y, list_z
|
|
+ );
|
|
+ }
|
|
+
|
|
+ private static final boolean DEBUG_SLICE_SHAPE = false;
|
|
+
|
|
+ public static VoxelShape sliceShape(final VoxelShape src, final Direction.Axis axis,
|
|
+ final int index) {
|
|
+ final VoxelShape ret = sliceShapeOptimised(src, axis, index);
|
|
+ if (DEBUG_SLICE_SHAPE) {
|
|
+ final VoxelShape vanilla = sliceShapeVanilla(src, axis, index);
|
|
+ if (!equals(ret, vanilla)) {
|
|
+ // special case: SliceShape is not empty when it should be!
|
|
+ if (areAnyFull(ret.shape) || areAnyFull(vanilla.shape)) {
|
|
+ equals(ret, vanilla);
|
|
+ sliceShapeOptimised(src, axis, index);
|
|
+ throw new IllegalStateException("Slice shape mismatch");
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ public static boolean voxelShapeIntersectNoEmpty(final VoxelShape voxel, final AABB aabb) {
|
|
if (voxel.isEmpty()) {
|
|
return false;
|
|
}
|
|
@@ -144,15 +398,15 @@ public final class CollisionUtil {
|
|
// note: this function assumes that for any i in coords that coord[i + 1] - coord[i] > COLLISION_EPSILON is true
|
|
|
|
// offsets that should be applied to coords
|
|
- final double off_x = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$offsetX();
|
|
- final double off_y = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$offsetY();
|
|
- final double off_z = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$offsetZ();
|
|
+ final double off_x = ((CollisionVoxelShape)voxel).moonrise$offsetX();
|
|
+ final double off_y = ((CollisionVoxelShape)voxel).moonrise$offsetY();
|
|
+ final double off_z = ((CollisionVoxelShape)voxel).moonrise$offsetZ();
|
|
|
|
- final double[] coords_x = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$rootCoordinatesX();
|
|
- final double[] coords_y = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$rootCoordinatesY();
|
|
- final double[] coords_z = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$rootCoordinatesZ();
|
|
+ final double[] coords_x = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesX();
|
|
+ final double[] coords_y = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesY();
|
|
+ final double[] coords_z = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesZ();
|
|
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData cached_shape_data = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$getCachedVoxelData();
|
|
+ final CachedShapeData cached_shape_data = ((CollisionVoxelShape)voxel).moonrise$getCachedVoxelData();
|
|
|
|
// note: size = coords.length - 1
|
|
final int size_x = cached_shape_data.sizeX();
|
|
@@ -246,23 +500,23 @@ public final class CollisionUtil {
|
|
}
|
|
|
|
// assume !target.isEmpty() && abs(source_move) >= COLLISION_EPSILON
|
|
- public static double collideX(final net.minecraft.world.phys.shapes.VoxelShape target, final net.minecraft.world.phys.AABB source, final double source_move) {
|
|
- final net.minecraft.world.phys.AABB single_aabb = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$getSingleAABBRepresentation();
|
|
+ public static double collideX(final VoxelShape target, final AABB source, final double source_move) {
|
|
+ final AABB single_aabb = ((CollisionVoxelShape)target).moonrise$getSingleAABBRepresentation();
|
|
if (single_aabb != null) {
|
|
return collideX(single_aabb, source, source_move);
|
|
}
|
|
// note: this function assumes that for any i in coords that coord[i + 1] - coord[i] > COLLISION_EPSILON is true
|
|
|
|
// offsets that should be applied to coords
|
|
- final double off_x = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$offsetX();
|
|
- final double off_y = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$offsetY();
|
|
- final double off_z = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$offsetZ();
|
|
+ final double off_x = ((CollisionVoxelShape)target).moonrise$offsetX();
|
|
+ final double off_y = ((CollisionVoxelShape)target).moonrise$offsetY();
|
|
+ final double off_z = ((CollisionVoxelShape)target).moonrise$offsetZ();
|
|
|
|
- final double[] coords_x = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$rootCoordinatesX();
|
|
- final double[] coords_y = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$rootCoordinatesY();
|
|
- final double[] coords_z = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$rootCoordinatesZ();
|
|
+ final double[] coords_x = ((CollisionVoxelShape)target).moonrise$rootCoordinatesX();
|
|
+ final double[] coords_y = ((CollisionVoxelShape)target).moonrise$rootCoordinatesY();
|
|
+ final double[] coords_z = ((CollisionVoxelShape)target).moonrise$rootCoordinatesZ();
|
|
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData cached_shape_data = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$getCachedVoxelData();
|
|
+ final CachedShapeData cached_shape_data = ((CollisionVoxelShape)target).moonrise$getCachedVoxelData();
|
|
|
|
// note: size = coords.length - 1
|
|
final int size_x = cached_shape_data.sizeX();
|
|
@@ -404,23 +658,23 @@ public final class CollisionUtil {
|
|
}
|
|
}
|
|
|
|
- public static double collideY(final net.minecraft.world.phys.shapes.VoxelShape target, final net.minecraft.world.phys.AABB source, final double source_move) {
|
|
- final net.minecraft.world.phys.AABB single_aabb = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$getSingleAABBRepresentation();
|
|
+ public static double collideY(final VoxelShape target, final AABB source, final double source_move) {
|
|
+ final AABB single_aabb = ((CollisionVoxelShape)target).moonrise$getSingleAABBRepresentation();
|
|
if (single_aabb != null) {
|
|
return collideY(single_aabb, source, source_move);
|
|
}
|
|
// note: this function assumes that for any i in coords that coord[i + 1] - coord[i] > COLLISION_EPSILON is true
|
|
|
|
// offsets that should be applied to coords
|
|
- final double off_x = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$offsetX();
|
|
- final double off_y = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$offsetY();
|
|
- final double off_z = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$offsetZ();
|
|
+ final double off_x = ((CollisionVoxelShape)target).moonrise$offsetX();
|
|
+ final double off_y = ((CollisionVoxelShape)target).moonrise$offsetY();
|
|
+ final double off_z = ((CollisionVoxelShape)target).moonrise$offsetZ();
|
|
|
|
- final double[] coords_x = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$rootCoordinatesX();
|
|
- final double[] coords_y = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$rootCoordinatesY();
|
|
- final double[] coords_z = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$rootCoordinatesZ();
|
|
+ final double[] coords_x = ((CollisionVoxelShape)target).moonrise$rootCoordinatesX();
|
|
+ final double[] coords_y = ((CollisionVoxelShape)target).moonrise$rootCoordinatesY();
|
|
+ final double[] coords_z = ((CollisionVoxelShape)target).moonrise$rootCoordinatesZ();
|
|
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData cached_shape_data = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$getCachedVoxelData();
|
|
+ final CachedShapeData cached_shape_data = ((CollisionVoxelShape)target).moonrise$getCachedVoxelData();
|
|
|
|
// note: size = coords.length - 1
|
|
final int size_x = cached_shape_data.sizeX();
|
|
@@ -562,23 +816,23 @@ public final class CollisionUtil {
|
|
}
|
|
}
|
|
|
|
- public static double collideZ(final net.minecraft.world.phys.shapes.VoxelShape target, final net.minecraft.world.phys.AABB source, final double source_move) {
|
|
- final net.minecraft.world.phys.AABB single_aabb = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$getSingleAABBRepresentation();
|
|
+ public static double collideZ(final VoxelShape target, final AABB source, final double source_move) {
|
|
+ final AABB single_aabb = ((CollisionVoxelShape)target).moonrise$getSingleAABBRepresentation();
|
|
if (single_aabb != null) {
|
|
return collideZ(single_aabb, source, source_move);
|
|
}
|
|
// note: this function assumes that for any i in coords that coord[i + 1] - coord[i] > COLLISION_EPSILON is true
|
|
|
|
// offsets that should be applied to coords
|
|
- final double off_x = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$offsetX();
|
|
- final double off_y = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$offsetY();
|
|
- final double off_z = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$offsetZ();
|
|
+ final double off_x = ((CollisionVoxelShape)target).moonrise$offsetX();
|
|
+ final double off_y = ((CollisionVoxelShape)target).moonrise$offsetY();
|
|
+ final double off_z = ((CollisionVoxelShape)target).moonrise$offsetZ();
|
|
|
|
- final double[] coords_x = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$rootCoordinatesX();
|
|
- final double[] coords_y = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$rootCoordinatesY();
|
|
- final double[] coords_z = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$rootCoordinatesZ();
|
|
+ final double[] coords_x = ((CollisionVoxelShape)target).moonrise$rootCoordinatesX();
|
|
+ final double[] coords_y = ((CollisionVoxelShape)target).moonrise$rootCoordinatesY();
|
|
+ final double[] coords_z = ((CollisionVoxelShape)target).moonrise$rootCoordinatesZ();
|
|
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData cached_shape_data = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)target).moonrise$getCachedVoxelData();
|
|
+ final CachedShapeData cached_shape_data = ((CollisionVoxelShape)target).moonrise$getCachedVoxelData();
|
|
|
|
// note: size = coords.length - 1
|
|
final int size_x = cached_shape_data.sizeX();
|
|
@@ -721,13 +975,13 @@ public final class CollisionUtil {
|
|
}
|
|
|
|
// does not use epsilon
|
|
- public static boolean strictlyContains(final net.minecraft.world.phys.shapes.VoxelShape voxel, final net.minecraft.world.phys.Vec3 point) {
|
|
+ public static boolean strictlyContains(final VoxelShape voxel, final Vec3 point) {
|
|
return strictlyContains(voxel, point.x, point.y, point.z);
|
|
}
|
|
|
|
// does not use epsilon
|
|
- public static boolean strictlyContains(final net.minecraft.world.phys.shapes.VoxelShape voxel, double x, double y, double z) {
|
|
- final net.minecraft.world.phys.AABB single_aabb = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();
|
|
+ public static boolean strictlyContains(final VoxelShape voxel, double x, double y, double z) {
|
|
+ final AABB single_aabb = ((CollisionVoxelShape)voxel).moonrise$getSingleAABBRepresentation();
|
|
if (single_aabb != null) {
|
|
return single_aabb.contains(x, y, z);
|
|
}
|
|
@@ -738,15 +992,15 @@ public final class CollisionUtil {
|
|
}
|
|
|
|
// offset input
|
|
- x -= ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$offsetX();
|
|
- y -= ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$offsetY();
|
|
- z -= ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$offsetZ();
|
|
+ x -= ((CollisionVoxelShape)voxel).moonrise$offsetX();
|
|
+ y -= ((CollisionVoxelShape)voxel).moonrise$offsetY();
|
|
+ z -= ((CollisionVoxelShape)voxel).moonrise$offsetZ();
|
|
|
|
- final double[] coords_x = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$rootCoordinatesX();
|
|
- final double[] coords_y = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$rootCoordinatesY();
|
|
- final double[] coords_z = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$rootCoordinatesZ();
|
|
+ final double[] coords_x = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesX();
|
|
+ final double[] coords_y = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesY();
|
|
+ final double[] coords_z = ((CollisionVoxelShape)voxel).moonrise$rootCoordinatesZ();
|
|
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData cached_shape_data = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)voxel).moonrise$getCachedVoxelData();
|
|
+ final CachedShapeData cached_shape_data = ((CollisionVoxelShape)voxel).moonrise$getCachedVoxelData();
|
|
|
|
// note: size = coords.length - 1
|
|
final int size_x = cached_shape_data.sizeX();
|
|
@@ -788,10 +1042,10 @@ public final class CollisionUtil {
|
|
return ((ft ? 1 : 0) << 1) | ((tf ? 1 : 0) << 2) | ((tt ? 1 : 0) << 3);
|
|
}
|
|
|
|
- private static net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape merge(final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData shapeDataFirst, final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData shapeDataSecond,
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedX, final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedY,
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedZ,
|
|
- final int booleanOp) {
|
|
+ private static BitSetDiscreteVoxelShape merge(final CachedShapeData shapeDataFirst, final CachedShapeData shapeDataSecond,
|
|
+ final MergedVoxelCoordinateList mergedX, final MergedVoxelCoordinateList mergedY,
|
|
+ final MergedVoxelCoordinateList mergedZ,
|
|
+ final int booleanOp) {
|
|
final int sizeX = mergedX.voxels;
|
|
final int sizeY = mergedY.voxels;
|
|
final int sizeZ = mergedZ.voxels;
|
|
@@ -806,7 +1060,7 @@ public final class CollisionUtil {
|
|
final int s2Mul2 = s2Mul1 * shapeDataSecond.sizeY();
|
|
|
|
// note: indices may contain -1, but nothing > size
|
|
- final net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape ret = new net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape(sizeX, sizeY, sizeZ);
|
|
+ final BitSetDiscreteVoxelShape ret = new BitSetDiscreteVoxelShape(sizeX, sizeY, sizeZ);
|
|
|
|
boolean empty = true;
|
|
|
|
@@ -823,10 +1077,11 @@ public final class CollisionUtil {
|
|
final int s1z = mergedZ.firstIndices[idxZ];
|
|
final int s2z = mergedZ.secondIndices[idxZ];
|
|
|
|
- int idx;
|
|
+ int idx1;
|
|
+ int idx2;
|
|
|
|
- final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx) & 1L);
|
|
- final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx) & 1L);
|
|
+ final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx1 = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx1) & 1L);
|
|
+ final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx2 = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx2) & 1L);
|
|
|
|
// idx ff -> 0
|
|
// idx ft -> 1
|
|
@@ -861,9 +1116,9 @@ public final class CollisionUtil {
|
|
return empty ? null : ret;
|
|
}
|
|
|
|
- private static boolean isMergeEmpty(final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData shapeDataFirst, final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData shapeDataSecond,
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedX, final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedY,
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedZ,
|
|
+ private static boolean isMergeEmpty(final CachedShapeData shapeDataFirst, final CachedShapeData shapeDataSecond,
|
|
+ final MergedVoxelCoordinateList mergedX, final MergedVoxelCoordinateList mergedY,
|
|
+ final MergedVoxelCoordinateList mergedZ,
|
|
final int booleanOp) {
|
|
final int sizeX = mergedX.voxels;
|
|
final int sizeY = mergedY.voxels;
|
|
@@ -889,10 +1144,11 @@ public final class CollisionUtil {
|
|
final int s1z = mergedZ.firstIndices[idxZ];
|
|
final int s2z = mergedZ.secondIndices[idxZ];
|
|
|
|
- int idx;
|
|
+ int idx1;
|
|
+ int idx2;
|
|
|
|
- final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx) & 1L);
|
|
- final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx) & 1L);
|
|
+ final int isS1Full = (s1x | s1y | s1z) < 0 ? 0 : (int)((s1Voxels[(idx1 = s1z + s1y*s1Mul1 + s1x*s1Mul2) >>> 6] >>> idx1) & 1L);
|
|
+ final int isS2Full = (s2x | s2y | s2z) < 0 ? 0 : (int)((s2Voxels[(idx2 = s2z + s2y*s2Mul1 + s2x*s2Mul2) >>> 6] >>> idx2) & 1L);
|
|
|
|
// idx ff -> 0
|
|
// idx ft -> 1
|
|
@@ -911,11 +1167,11 @@ public final class CollisionUtil {
|
|
return true;
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.shapes.VoxelShape joinOptimized(final net.minecraft.world.phys.shapes.VoxelShape first, final net.minecraft.world.phys.shapes.VoxelShape second, final net.minecraft.world.phys.shapes.BooleanOp operator) {
|
|
+ public static VoxelShape joinOptimized(final VoxelShape first, final VoxelShape second, final BooleanOp operator) {
|
|
return joinUnoptimized(first, second, operator).optimize();
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.shapes.VoxelShape joinUnoptimized(final net.minecraft.world.phys.shapes.VoxelShape first, final net.minecraft.world.phys.shapes.VoxelShape second, final net.minecraft.world.phys.shapes.BooleanOp operator) {
|
|
+ public static VoxelShape joinUnoptimized(final VoxelShape first, final VoxelShape second, final BooleanOp operator) {
|
|
final boolean ff = operator.apply(false, false);
|
|
if (ff) {
|
|
// technically, should be an infinite box but that's clearly an error
|
|
@@ -925,23 +1181,23 @@ public final class CollisionUtil {
|
|
final boolean tt = operator.apply(true, true);
|
|
|
|
if (first == second) {
|
|
- return tt ? first : net.minecraft.world.phys.shapes.Shapes.empty();
|
|
+ return tt ? first : Shapes.empty();
|
|
}
|
|
|
|
final boolean ft = operator.apply(false, true);
|
|
final boolean tf = operator.apply(true, false);
|
|
|
|
if (first.isEmpty()) {
|
|
- return ft ? second : net.minecraft.world.phys.shapes.Shapes.empty();
|
|
+ return ft ? second : Shapes.empty();
|
|
}
|
|
if (second.isEmpty()) {
|
|
- return tf ? first : net.minecraft.world.phys.shapes.Shapes.empty();
|
|
+ return tf ? first : Shapes.empty();
|
|
}
|
|
|
|
if (!tt) {
|
|
// try to check for no intersection, since tt = false
|
|
- final net.minecraft.world.phys.AABB aabbF = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$getSingleAABBRepresentation();
|
|
- final net.minecraft.world.phys.AABB aabbS = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$getSingleAABBRepresentation();
|
|
+ final AABB aabbF = ((CollisionVoxelShape)first).moonrise$getSingleAABBRepresentation();
|
|
+ final AABB aabbS = ((CollisionVoxelShape)second).moonrise$getSingleAABBRepresentation();
|
|
|
|
final boolean intersect;
|
|
|
|
@@ -962,7 +1218,7 @@ public final class CollisionUtil {
|
|
|
|
if (!intersect) {
|
|
if (!tf & !ft) {
|
|
- return net.minecraft.world.phys.shapes.Shapes.empty();
|
|
+ return Shapes.empty();
|
|
}
|
|
if (!tf | !ft) {
|
|
return tf ? first : second;
|
|
@@ -970,50 +1226,50 @@ public final class CollisionUtil {
|
|
}
|
|
}
|
|
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedX = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.merge(
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$rootCoordinatesX(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$offsetX(),
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$offsetX(),
|
|
+ final MergedVoxelCoordinateList mergedX = MergedVoxelCoordinateList.merge(
|
|
+ ((CollisionVoxelShape)first).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)first).moonrise$offsetX(),
|
|
+ ((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
|
|
ft, tf
|
|
);
|
|
- if (mergedX == ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.EMPTY) {
|
|
- return net.minecraft.world.phys.shapes.Shapes.empty();
|
|
+ if (mergedX == null) {
|
|
+ return Shapes.empty();
|
|
}
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedY = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.merge(
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$rootCoordinatesY(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$offsetY(),
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$offsetY(),
|
|
+ final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge(
|
|
+ ((CollisionVoxelShape)first).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)first).moonrise$offsetY(),
|
|
+ ((CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)second).moonrise$offsetY(),
|
|
ft, tf
|
|
);
|
|
- if (mergedY == ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.EMPTY) {
|
|
- return net.minecraft.world.phys.shapes.Shapes.empty();
|
|
+ if (mergedY == null) {
|
|
+ return Shapes.empty();
|
|
}
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedZ = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.merge(
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$rootCoordinatesZ(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$offsetZ(),
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$offsetZ(),
|
|
+ final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge(
|
|
+ ((CollisionVoxelShape)first).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)first).moonrise$offsetZ(),
|
|
+ ((CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)second).moonrise$offsetZ(),
|
|
ft, tf
|
|
);
|
|
- if (mergedZ == ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.EMPTY) {
|
|
- return net.minecraft.world.phys.shapes.Shapes.empty();
|
|
+ if (mergedZ == null) {
|
|
+ return Shapes.empty();
|
|
}
|
|
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData shapeDataFirst = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$getCachedVoxelData();
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData shapeDataSecond = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$getCachedVoxelData();
|
|
+ final CachedShapeData shapeDataFirst = ((CollisionVoxelShape)first).moonrise$getCachedVoxelData();
|
|
+ final CachedShapeData shapeDataSecond = ((CollisionVoxelShape)second).moonrise$getCachedVoxelData();
|
|
|
|
- final net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape mergedShape = merge(
|
|
+ final BitSetDiscreteVoxelShape mergedShape = merge(
|
|
shapeDataFirst, shapeDataSecond,
|
|
mergedX, mergedY, mergedZ,
|
|
makeBitset(ft, tf, tt)
|
|
);
|
|
|
|
if (mergedShape == null) {
|
|
- return net.minecraft.world.phys.shapes.Shapes.empty();
|
|
+ return Shapes.empty();
|
|
}
|
|
|
|
- return new net.minecraft.world.phys.shapes.ArrayVoxelShape(
|
|
+ return new ArrayVoxelShape(
|
|
mergedShape, mergedX.wrapCoords(), mergedY.wrapCoords(), mergedZ.wrapCoords()
|
|
);
|
|
}
|
|
|
|
- public static boolean isJoinNonEmpty(final net.minecraft.world.phys.shapes.VoxelShape first, final net.minecraft.world.phys.shapes.VoxelShape second, final net.minecraft.world.phys.shapes.BooleanOp operator) {
|
|
+ public static boolean isJoinNonEmpty(final VoxelShape first, final VoxelShape second, final BooleanOp operator) {
|
|
final boolean ff = operator.apply(false, false);
|
|
if (ff) {
|
|
// technically, should be an infinite box but that's clearly an error
|
|
@@ -1035,8 +1291,8 @@ public final class CollisionUtil {
|
|
final boolean tf = operator.apply(true, false);
|
|
|
|
// try to check intersection
|
|
- final net.minecraft.world.phys.AABB aabbF = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$getSingleAABBRepresentation();
|
|
- final net.minecraft.world.phys.AABB aabbS = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$getSingleAABBRepresentation();
|
|
+ final AABB aabbF = ((CollisionVoxelShape)first).moonrise$getSingleAABBRepresentation();
|
|
+ final AABB aabbS = ((CollisionVoxelShape)second).moonrise$getSingleAABBRepresentation();
|
|
|
|
final boolean intersect;
|
|
|
|
@@ -1068,33 +1324,33 @@ public final class CollisionUtil {
|
|
}
|
|
}
|
|
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedX = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.merge(
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$rootCoordinatesX(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$offsetX(),
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$offsetX(),
|
|
+ final MergedVoxelCoordinateList mergedX = MergedVoxelCoordinateList.merge(
|
|
+ ((CollisionVoxelShape)first).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)first).moonrise$offsetX(),
|
|
+ ((CollisionVoxelShape)second).moonrise$rootCoordinatesX(), ((CollisionVoxelShape)second).moonrise$offsetX(),
|
|
ft, tf
|
|
);
|
|
- if (mergedX == ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.EMPTY) {
|
|
+ if (mergedX == null) {
|
|
return false;
|
|
}
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedY = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.merge(
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$rootCoordinatesY(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$offsetY(),
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$offsetY(),
|
|
+ final MergedVoxelCoordinateList mergedY = MergedVoxelCoordinateList.merge(
|
|
+ ((CollisionVoxelShape)first).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)first).moonrise$offsetY(),
|
|
+ ((CollisionVoxelShape)second).moonrise$rootCoordinatesY(), ((CollisionVoxelShape)second).moonrise$offsetY(),
|
|
ft, tf
|
|
);
|
|
- if (mergedY == ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.EMPTY) {
|
|
+ if (mergedY == null) {
|
|
return false;
|
|
}
|
|
- final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList mergedZ = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.merge(
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$rootCoordinatesZ(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$offsetZ(),
|
|
- ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$offsetZ(),
|
|
+ final MergedVoxelCoordinateList mergedZ = MergedVoxelCoordinateList.merge(
|
|
+ ((CollisionVoxelShape)first).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)first).moonrise$offsetZ(),
|
|
+ ((CollisionVoxelShape)second).moonrise$rootCoordinatesZ(), ((CollisionVoxelShape)second).moonrise$offsetZ(),
|
|
ft, tf
|
|
);
|
|
- if (mergedZ == ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList.EMPTY) {
|
|
+ if (mergedZ == null) {
|
|
return false;
|
|
}
|
|
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData shapeDataFirst = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)first).moonrise$getCachedVoxelData();
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData shapeDataSecond = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)second).moonrise$getCachedVoxelData();
|
|
+ final CachedShapeData shapeDataFirst = ((CollisionVoxelShape)first).moonrise$getCachedVoxelData();
|
|
+ final CachedShapeData shapeDataSecond = ((CollisionVoxelShape)second).moonrise$getCachedVoxelData();
|
|
|
|
return !isMergeEmpty(
|
|
shapeDataFirst, shapeDataSecond,
|
|
@@ -1112,10 +1368,6 @@ public final class CollisionUtil {
|
|
}
|
|
}
|
|
|
|
- private static final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList EMPTY = new ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList(
|
|
- new double[] { 0.0 }, 0.0, new int[0], new int[0], 0
|
|
- );
|
|
-
|
|
private static int[] getIndices(final int length) {
|
|
final int[] ret = new int[length];
|
|
|
|
@@ -1142,25 +1394,25 @@ public final class CollisionUtil {
|
|
this.voxels = voxels;
|
|
}
|
|
|
|
- public it.unimi.dsi.fastutil.doubles.DoubleList wrapCoords() {
|
|
+ public DoubleList wrapCoords() {
|
|
if (this.coordinateOffset == 0.0) {
|
|
- return it.unimi.dsi.fastutil.doubles.DoubleArrayList.wrap(this.coordinates, this.voxels + 1);
|
|
+ return DoubleArrayList.wrap(this.coordinates, this.voxels + 1);
|
|
}
|
|
- return new net.minecraft.world.phys.shapes.OffsetDoubleList(it.unimi.dsi.fastutil.doubles.DoubleArrayList.wrap(this.coordinates, this.voxels + 1), this.coordinateOffset);
|
|
+ return new OffsetDoubleList(DoubleArrayList.wrap(this.coordinates, this.voxels + 1), this.coordinateOffset);
|
|
}
|
|
|
|
// assume coordinates.length > 1
|
|
- public static ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList getForSingle(final double[] coordinates, final double offset) {
|
|
+ public static MergedVoxelCoordinateList getForSingle(final double[] coordinates, final double offset) {
|
|
final int voxels = coordinates.length - 1;
|
|
final int[] indices = voxels < SIMPLE_INDICES_CACHE.length ? SIMPLE_INDICES_CACHE[voxels] : getIndices(voxels);
|
|
|
|
- return new ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList(coordinates, offset, indices, indices, voxels);
|
|
+ return new MergedVoxelCoordinateList(coordinates, offset, indices, indices, voxels);
|
|
}
|
|
|
|
// assume coordinates.length > 1
|
|
- public static ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList merge(final double[] firstCoordinates, final double firstOffset,
|
|
- final double[] secondCoordinates, final double secondOffset,
|
|
- final boolean ft, final boolean tf) {
|
|
+ public static MergedVoxelCoordinateList merge(final double[] firstCoordinates, final double firstOffset,
|
|
+ final double[] secondCoordinates, final double secondOffset,
|
|
+ final boolean ft, final boolean tf) {
|
|
if (firstCoordinates == secondCoordinates && firstOffset == secondOffset) {
|
|
return getForSingle(firstCoordinates, firstOffset);
|
|
}
|
|
@@ -1250,13 +1502,13 @@ public final class CollisionUtil {
|
|
}
|
|
}
|
|
|
|
- return resultSize <= 1 ? EMPTY : new ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.MergedVoxelCoordinateList(coordinates, 0.0, firstIndices, secondIndices, resultSize - 1);
|
|
+ return resultSize <= 1 ? null : new MergedVoxelCoordinateList(coordinates, 0.0, firstIndices, secondIndices, resultSize - 1);
|
|
}
|
|
}
|
|
|
|
- public static boolean equals(final net.minecraft.world.phys.shapes.DiscreteVoxelShape shape1, final net.minecraft.world.phys.shapes.DiscreteVoxelShape shape2) {
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData cachedShapeData1 = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionDiscreteVoxelShape)shape1).moonrise$getOrCreateCachedShapeData();
|
|
- final ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData cachedShapeData2 = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionDiscreteVoxelShape)shape2).moonrise$getOrCreateCachedShapeData();
|
|
+ public static boolean equals(final DiscreteVoxelShape shape1, final DiscreteVoxelShape shape2) {
|
|
+ final CachedShapeData cachedShapeData1 = ((CollisionDiscreteVoxelShape)shape1).moonrise$getOrCreateCachedShapeData();
|
|
+ final CachedShapeData cachedShapeData2 = ((CollisionDiscreteVoxelShape)shape2).moonrise$getOrCreateCachedShapeData();
|
|
|
|
final boolean isEmpty1 = cachedShapeData1.isEmpty();
|
|
final boolean isEmpty2 = cachedShapeData2.isEmpty();
|
|
@@ -1265,7 +1517,7 @@ public final class CollisionUtil {
|
|
return true;
|
|
} else if (isEmpty1 ^ isEmpty2) {
|
|
return false;
|
|
- }
|
|
+ } // else: isEmpty1 = isEmpty2 = false
|
|
|
|
if (cachedShapeData1.hasSingleAABB() != cachedShapeData2.hasSingleAABB()) {
|
|
return false;
|
|
@@ -1281,153 +1533,237 @@ public final class CollisionUtil {
|
|
return false;
|
|
}
|
|
|
|
- return java.util.Arrays.equals(cachedShapeData1.voxelSet(), cachedShapeData2.voxelSet());
|
|
+ return Arrays.equals(cachedShapeData1.voxelSet(), cachedShapeData2.voxelSet());
|
|
}
|
|
|
|
// useful only for testing
|
|
- public static boolean equals(final net.minecraft.world.phys.shapes.VoxelShape shape1, final net.minecraft.world.phys.shapes.VoxelShape shape2) {
|
|
+ public static boolean equals(final VoxelShape shape1, final VoxelShape shape2) {
|
|
+ if (shape1.isEmpty() & shape2.isEmpty()) {
|
|
+ return true;
|
|
+ } else if (shape1.isEmpty() ^ shape2.isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
if (!equals(shape1.shape, shape2.shape)) {
|
|
return false;
|
|
}
|
|
|
|
- return shape1.getCoords(net.minecraft.core.Direction.Axis.X).equals(shape2.getCoords(net.minecraft.core.Direction.Axis.X)) &&
|
|
- shape1.getCoords(net.minecraft.core.Direction.Axis.Y).equals(shape2.getCoords(net.minecraft.core.Direction.Axis.Y)) &&
|
|
- shape1.getCoords(net.minecraft.core.Direction.Axis.Z).equals(shape2.getCoords(net.minecraft.core.Direction.Axis.Z));
|
|
+ return shape1.getCoords(Direction.Axis.X).equals(shape2.getCoords(Direction.Axis.X)) &&
|
|
+ shape1.getCoords(Direction.Axis.Y).equals(shape2.getCoords(Direction.Axis.Y)) &&
|
|
+ shape1.getCoords(Direction.Axis.Z).equals(shape2.getCoords(Direction.Axis.Z));
|
|
+ }
|
|
+
|
|
+ public static boolean areAnyFull(final DiscreteVoxelShape shape) {
|
|
+ if (shape.isEmpty()) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ final int sizeX = shape.getXSize();
|
|
+ final int sizeY = shape.getYSize();
|
|
+ final int sizeZ = shape.getZSize();
|
|
+
|
|
+ for (int x = 0; x < sizeX; ++x) {
|
|
+ for (int y = 0; y < sizeY; ++y) {
|
|
+ for (int z = 0; z < sizeZ; ++z) {
|
|
+ if (shape.isFull(x, y, z)) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ public static String shapeMismatch(final DiscreteVoxelShape shape1, final DiscreteVoxelShape shape2) {
|
|
+ final CachedShapeData cachedShapeData1 = ((CollisionDiscreteVoxelShape)shape1).moonrise$getOrCreateCachedShapeData();
|
|
+ final CachedShapeData cachedShapeData2 = ((CollisionDiscreteVoxelShape)shape2).moonrise$getOrCreateCachedShapeData();
|
|
+
|
|
+ final boolean isEmpty1 = cachedShapeData1.isEmpty();
|
|
+ final boolean isEmpty2 = cachedShapeData2.isEmpty();
|
|
+
|
|
+ if (isEmpty1 & isEmpty2) {
|
|
+ return null;
|
|
+ } else if (isEmpty1 ^ isEmpty2) {
|
|
+ return null;
|
|
+ } // else: isEmpty1 = isEmpty2 = false
|
|
+
|
|
+ if (cachedShapeData1.sizeX() != cachedShapeData2.sizeX()) {
|
|
+ return "size x: " + cachedShapeData1.sizeX() + " != " + cachedShapeData2.sizeX();
|
|
+ }
|
|
+ if (cachedShapeData1.sizeY() != cachedShapeData2.sizeY()) {
|
|
+ return "size y: " + cachedShapeData1.sizeY() + " != " + cachedShapeData2.sizeY();
|
|
+ }
|
|
+ if (cachedShapeData1.sizeZ() != cachedShapeData2.sizeZ()) {
|
|
+ return "size z: " + cachedShapeData1.sizeZ() + " != " + cachedShapeData2.sizeZ();
|
|
+ }
|
|
+
|
|
+ final StringBuilder ret = new StringBuilder();
|
|
+
|
|
+ final int sizeX = cachedShapeData1.sizeX();;
|
|
+ final int sizeY = cachedShapeData1.sizeY();
|
|
+ final int sizeZ = cachedShapeData1.sizeZ();
|
|
+
|
|
+ boolean first = true;
|
|
+
|
|
+ for (int x = 0; x < sizeX; ++x) {
|
|
+ for (int y = 0; y < sizeY; ++y) {
|
|
+ for (int z = 0; z < sizeZ; ++z) {
|
|
+ final boolean isFull1 = shape1.isFull(x, y, z);
|
|
+ final boolean isFull2 = shape2.isFull(x, y, z);
|
|
+
|
|
+ if (isFull1 == isFull2) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (first) {
|
|
+ first = false;
|
|
+ } else {
|
|
+ ret.append(", ");
|
|
+ }
|
|
+
|
|
+ ret.append("(").append(x).append(",").append(y).append(",").append(z)
|
|
+ .append("): shape1: ").append(isFull1).append(", shape2: ").append(isFull2);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret.isEmpty() ? null : ret.toString();
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB offsetX(final net.minecraft.world.phys.AABB box, final double dx) {
|
|
- return new net.minecraft.world.phys.AABB(box.minX + dx, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ);
|
|
+ public static AABB offsetX(final AABB box, final double dx) {
|
|
+ return new AABB(box.minX + dx, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB offsetY(final net.minecraft.world.phys.AABB box, final double dy) {
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY + dy, box.minZ, box.maxX, box.maxY + dy, box.maxZ);
|
|
+ public static AABB offsetY(final AABB box, final double dy) {
|
|
+ return new AABB(box.minX, box.minY + dy, box.minZ, box.maxX, box.maxY + dy, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB offsetZ(final net.minecraft.world.phys.AABB box, final double dz) {
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY, box.minZ + dz, box.maxX, box.maxY, box.maxZ + dz);
|
|
+ public static AABB offsetZ(final AABB box, final double dz) {
|
|
+ return new AABB(box.minX, box.minY, box.minZ + dz, box.maxX, box.maxY, box.maxZ + dz);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB expandRight(final net.minecraft.world.phys.AABB box, final double dx) { // dx > 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ);
|
|
+ public static AABB expandRight(final AABB box, final double dx) { // dx > 0.0
|
|
+ return new AABB(box.minX, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB expandLeft(final net.minecraft.world.phys.AABB box, final double dx) { // dx < 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX - dx, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ);
|
|
+ public static AABB expandLeft(final AABB box, final double dx) { // dx < 0.0
|
|
+ return new AABB(box.minX - dx, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB expandUpwards(final net.minecraft.world.phys.AABB box, final double dy) { // dy > 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY, box.minZ, box.maxX, box.maxY + dy, box.maxZ);
|
|
+ public static AABB expandUpwards(final AABB box, final double dy) { // dy > 0.0
|
|
+ return new AABB(box.minX, box.minY, box.minZ, box.maxX, box.maxY + dy, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB expandDownwards(final net.minecraft.world.phys.AABB box, final double dy) { // dy < 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY - dy, box.minZ, box.maxX, box.maxY, box.maxZ);
|
|
+ public static AABB expandDownwards(final AABB box, final double dy) { // dy < 0.0
|
|
+ return new AABB(box.minX, box.minY - dy, box.minZ, box.maxX, box.maxY, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB expandForwards(final net.minecraft.world.phys.AABB box, final double dz) { // dz > 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ + dz);
|
|
+ public static AABB expandForwards(final AABB box, final double dz) { // dz > 0.0
|
|
+ return new AABB(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ + dz);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB expandBackwards(final net.minecraft.world.phys.AABB box, final double dz) { // dz < 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY, box.minZ - dz, box.maxX, box.maxY, box.maxZ);
|
|
+ public static AABB expandBackwards(final AABB box, final double dz) { // dz < 0.0
|
|
+ return new AABB(box.minX, box.minY, box.minZ - dz, box.maxX, box.maxY, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB cutRight(final net.minecraft.world.phys.AABB box, final double dx) { // dx > 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.maxX, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ);
|
|
+ public static AABB cutRight(final AABB box, final double dx) { // dx > 0.0
|
|
+ return new AABB(box.maxX, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB cutLeft(final net.minecraft.world.phys.AABB box, final double dx) { // dx < 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX + dx, box.minY, box.minZ, box.minX, box.maxY, box.maxZ);
|
|
+ public static AABB cutLeft(final AABB box, final double dx) { // dx < 0.0
|
|
+ return new AABB(box.minX + dx, box.minY, box.minZ, box.minX, box.maxY, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB cutUpwards(final net.minecraft.world.phys.AABB box, final double dy) { // dy > 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.maxY, box.minZ, box.maxX, box.maxY + dy, box.maxZ);
|
|
+ public static AABB cutUpwards(final AABB box, final double dy) { // dy > 0.0
|
|
+ return new AABB(box.minX, box.maxY, box.minZ, box.maxX, box.maxY + dy, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB cutDownwards(final net.minecraft.world.phys.AABB box, final double dy) { // dy < 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY + dy, box.minZ, box.maxX, box.minY, box.maxZ);
|
|
+ public static AABB cutDownwards(final AABB box, final double dy) { // dy < 0.0
|
|
+ return new AABB(box.minX, box.minY + dy, box.minZ, box.maxX, box.minY, box.maxZ);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB cutForwards(final net.minecraft.world.phys.AABB box, final double dz) { // dz > 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY, box.maxZ, box.maxX, box.maxY, box.maxZ + dz);
|
|
+ public static AABB cutForwards(final AABB box, final double dz) { // dz > 0.0
|
|
+ return new AABB(box.minX, box.minY, box.maxZ, box.maxX, box.maxY, box.maxZ + dz);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.AABB cutBackwards(final net.minecraft.world.phys.AABB box, final double dz) { // dz < 0.0
|
|
- return new net.minecraft.world.phys.AABB(box.minX, box.minY, box.minZ + dz, box.maxX, box.maxY, box.minZ);
|
|
+ public static AABB cutBackwards(final AABB box, final double dz) { // dz < 0.0
|
|
+ return new AABB(box.minX, box.minY, box.minZ + dz, box.maxX, box.maxY, box.minZ);
|
|
}
|
|
|
|
- public static double performAABBCollisionsX(final net.minecraft.world.phys.AABB currentBoundingBox, double value, final java.util.List<net.minecraft.world.phys.AABB> potentialCollisions) {
|
|
+ public static double performAABBCollisionsX(final AABB currentBoundingBox, double value, final List<AABB> potentialCollisions) {
|
|
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
|
|
if (Math.abs(value) < COLLISION_EPSILON) {
|
|
return 0.0;
|
|
}
|
|
- final net.minecraft.world.phys.AABB target = potentialCollisions.get(i);
|
|
+ final AABB target = potentialCollisions.get(i);
|
|
value = collideX(target, currentBoundingBox, value);
|
|
}
|
|
|
|
- return value;
|
|
+ return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value;
|
|
}
|
|
|
|
- public static double performAABBCollisionsY(final net.minecraft.world.phys.AABB currentBoundingBox, double value, final java.util.List<net.minecraft.world.phys.AABB> potentialCollisions) {
|
|
+ public static double performAABBCollisionsY(final AABB currentBoundingBox, double value, final List<AABB> potentialCollisions) {
|
|
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
|
|
if (Math.abs(value) < COLLISION_EPSILON) {
|
|
return 0.0;
|
|
}
|
|
- final net.minecraft.world.phys.AABB target = potentialCollisions.get(i);
|
|
+ final AABB target = potentialCollisions.get(i);
|
|
value = collideY(target, currentBoundingBox, value);
|
|
}
|
|
|
|
- return value;
|
|
+ return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value;
|
|
}
|
|
|
|
- public static double performAABBCollisionsZ(final net.minecraft.world.phys.AABB currentBoundingBox, double value, final java.util.List<net.minecraft.world.phys.AABB> potentialCollisions) {
|
|
+ public static double performAABBCollisionsZ(final AABB currentBoundingBox, double value, final List<AABB> potentialCollisions) {
|
|
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
|
|
if (Math.abs(value) < COLLISION_EPSILON) {
|
|
return 0.0;
|
|
}
|
|
- final net.minecraft.world.phys.AABB target = potentialCollisions.get(i);
|
|
+ final AABB target = potentialCollisions.get(i);
|
|
value = collideZ(target, currentBoundingBox, value);
|
|
}
|
|
|
|
- return value;
|
|
+ return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value;
|
|
}
|
|
|
|
- public static double performVoxelCollisionsX(final net.minecraft.world.phys.AABB currentBoundingBox, double value, final java.util.List<net.minecraft.world.phys.shapes.VoxelShape> potentialCollisions) {
|
|
+ public static double performVoxelCollisionsX(final AABB currentBoundingBox, double value, final List<VoxelShape> potentialCollisions) {
|
|
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
|
|
if (Math.abs(value) < COLLISION_EPSILON) {
|
|
return 0.0;
|
|
}
|
|
- final net.minecraft.world.phys.shapes.VoxelShape target = potentialCollisions.get(i);
|
|
+ final VoxelShape target = potentialCollisions.get(i);
|
|
value = collideX(target, currentBoundingBox, value);
|
|
}
|
|
|
|
- return value;
|
|
+ return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value;
|
|
}
|
|
|
|
- public static double performVoxelCollisionsY(final net.minecraft.world.phys.AABB currentBoundingBox, double value, final java.util.List<net.minecraft.world.phys.shapes.VoxelShape> potentialCollisions) {
|
|
+ public static double performVoxelCollisionsY(final AABB currentBoundingBox, double value, final List<VoxelShape> potentialCollisions) {
|
|
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
|
|
if (Math.abs(value) < COLLISION_EPSILON) {
|
|
return 0.0;
|
|
}
|
|
- final net.minecraft.world.phys.shapes.VoxelShape target = potentialCollisions.get(i);
|
|
+ final VoxelShape target = potentialCollisions.get(i);
|
|
value = collideY(target, currentBoundingBox, value);
|
|
}
|
|
|
|
- return value;
|
|
+ return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value;
|
|
}
|
|
|
|
- public static double performVoxelCollisionsZ(final net.minecraft.world.phys.AABB currentBoundingBox, double value, final java.util.List<net.minecraft.world.phys.shapes.VoxelShape> potentialCollisions) {
|
|
+ public static double performVoxelCollisionsZ(final AABB currentBoundingBox, double value, final List<VoxelShape> potentialCollisions) {
|
|
for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
|
|
if (Math.abs(value) < COLLISION_EPSILON) {
|
|
return 0.0;
|
|
}
|
|
- final net.minecraft.world.phys.shapes.VoxelShape target = potentialCollisions.get(i);
|
|
+ final VoxelShape target = potentialCollisions.get(i);
|
|
value = collideZ(target, currentBoundingBox, value);
|
|
}
|
|
|
|
- return value;
|
|
+ return Math.abs(value) < COLLISION_EPSILON ? 0.0 : value;
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.Vec3 performVoxelCollisions(final net.minecraft.world.phys.Vec3 moveVector, net.minecraft.world.phys.AABB axisalignedbb, final java.util.List<net.minecraft.world.phys.shapes.VoxelShape> potentialCollisions) {
|
|
+ public static Vec3 performVoxelCollisions(final Vec3 moveVector, AABB axisalignedbb, final List<VoxelShape> potentialCollisions) {
|
|
double x = moveVector.x;
|
|
double y = moveVector.y;
|
|
double z = moveVector.z;
|
|
@@ -1459,10 +1795,10 @@ public final class CollisionUtil {
|
|
z = performVoxelCollisionsZ(axisalignedbb, z, potentialCollisions);
|
|
}
|
|
|
|
- return new net.minecraft.world.phys.Vec3(x, y, z);
|
|
+ return new Vec3(x, y, z);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.Vec3 performAABBCollisions(final net.minecraft.world.phys.Vec3 moveVector, net.minecraft.world.phys.AABB axisalignedbb, final java.util.List<net.minecraft.world.phys.AABB> potentialCollisions) {
|
|
+ public static Vec3 performAABBCollisions(final Vec3 moveVector, AABB axisalignedbb, final List<AABB> potentialCollisions) {
|
|
double x = moveVector.x;
|
|
double y = moveVector.y;
|
|
double z = moveVector.z;
|
|
@@ -1494,12 +1830,12 @@ public final class CollisionUtil {
|
|
z = performAABBCollisionsZ(axisalignedbb, z, potentialCollisions);
|
|
}
|
|
|
|
- return new net.minecraft.world.phys.Vec3(x, y, z);
|
|
+ return new Vec3(x, y, z);
|
|
}
|
|
|
|
- public static net.minecraft.world.phys.Vec3 performCollisions(final net.minecraft.world.phys.Vec3 moveVector, net.minecraft.world.phys.AABB axisalignedbb,
|
|
- final java.util.List<net.minecraft.world.phys.shapes.VoxelShape> voxels,
|
|
- final java.util.List<net.minecraft.world.phys.AABB> aabbs) {
|
|
+ public static Vec3 performCollisions(final Vec3 moveVector, AABB axisalignedbb,
|
|
+ final List<VoxelShape> voxels,
|
|
+ final List<AABB> aabbs) {
|
|
if (voxels.isEmpty()) {
|
|
// fast track only AABBs
|
|
return performAABBCollisions(moveVector, axisalignedbb, aabbs);
|
|
@@ -1540,14 +1876,14 @@ public final class CollisionUtil {
|
|
z = performVoxelCollisionsZ(axisalignedbb, z, voxels);
|
|
}
|
|
|
|
- return new net.minecraft.world.phys.Vec3(x, y, z);
|
|
+ return new Vec3(x, y, z);
|
|
}
|
|
|
|
- public static boolean isCollidingWithBorder(final net.minecraft.world.level.border.WorldBorder worldborder, final net.minecraft.world.phys.AABB boundingBox) {
|
|
+ public static boolean isCollidingWithBorder(final WorldBorder worldborder, final AABB boundingBox) {
|
|
return isCollidingWithBorder(worldborder, boundingBox.minX, boundingBox.maxX, boundingBox.minZ, boundingBox.maxZ);
|
|
}
|
|
|
|
- public static boolean isCollidingWithBorder(final net.minecraft.world.level.border.WorldBorder worldborder,
|
|
+ public static boolean isCollidingWithBorder(final WorldBorder worldborder,
|
|
final double boxMinX, final double boxMaxX,
|
|
final double boxMinZ, final double boxMaxZ) {
|
|
final double borderMinX = Math.floor(worldborder.getMinX()); // -X
|
|
@@ -1557,8 +1893,8 @@ public final class CollisionUtil {
|
|
final double borderMaxZ = Math.ceil(worldborder.getMaxZ()); // +Z
|
|
|
|
// inverted check for world border enclosing the specified box expanded by -EPSILON
|
|
- return (borderMinX - boxMinX) > ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON || (borderMaxX - boxMaxX) < -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON ||
|
|
- (borderMinZ - boxMinZ) > ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON || (borderMaxZ - boxMaxZ) < -ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON;
|
|
+ return (borderMinX - boxMinX) > CollisionUtil.COLLISION_EPSILON || (borderMaxX - boxMaxX) < -CollisionUtil.COLLISION_EPSILON ||
|
|
+ (borderMinZ - boxMinZ) > CollisionUtil.COLLISION_EPSILON || (borderMaxZ - boxMaxZ) < -CollisionUtil.COLLISION_EPSILON;
|
|
}
|
|
|
|
/* Math.max/min specify that any NaN argument results in a NaN return, unlike these functions */
|
|
@@ -1575,38 +1911,38 @@ public final class CollisionUtil {
|
|
public static final int COLLISION_FLAG_CHECK_BORDER = 1 << 2;
|
|
public static final int COLLISION_FLAG_CHECK_ONLY = 1 << 3;
|
|
|
|
- public static boolean getCollisionsForBlocksOrWorldBorder(final net.minecraft.world.level.Level world, final net.minecraft.world.entity.Entity entity, final net.minecraft.world.phys.AABB aabb,
|
|
- final java.util.List<net.minecraft.world.phys.shapes.VoxelShape> intoVoxel, final java.util.List<net.minecraft.world.phys.AABB> intoAABB,
|
|
- final int collisionFlags, final java.util.function.BiPredicate<net.minecraft.world.level.block.state.BlockState, net.minecraft.core.BlockPos> predicate) {
|
|
+ public static boolean getCollisionsForBlocksOrWorldBorder(final Level world, final Entity entity, final AABB aabb,
|
|
+ final List<VoxelShape> intoVoxel, final List<AABB> intoAABB,
|
|
+ final int collisionFlags, final BiPredicate<BlockState, BlockPos> predicate) {
|
|
final boolean checkOnly = (collisionFlags & COLLISION_FLAG_CHECK_ONLY) != 0;
|
|
boolean ret = false;
|
|
|
|
if ((collisionFlags & COLLISION_FLAG_CHECK_BORDER) != 0) {
|
|
- final net.minecraft.world.level.border.WorldBorder worldBorder = world.getWorldBorder();
|
|
- if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isCollidingWithBorder(worldBorder, aabb) && entity != null && worldBorder.isInsideCloseToBorder(entity, aabb)) {
|
|
+ final WorldBorder worldBorder = world.getWorldBorder();
|
|
+ if (CollisionUtil.isCollidingWithBorder(worldBorder, aabb) && entity != null && worldBorder.isInsideCloseToBorder(entity, aabb)) {
|
|
if (checkOnly) {
|
|
return true;
|
|
} else {
|
|
- final net.minecraft.world.phys.shapes.VoxelShape borderShape = worldBorder.getCollisionShape();
|
|
+ final VoxelShape borderShape = worldBorder.getCollisionShape();
|
|
intoVoxel.add(borderShape);
|
|
ret = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
- final int minSection = ((ca.spottedleaf.moonrise.patches.collisions.world.CollisionLevel)world).moonrise$getMinSection();
|
|
+ final int minSection = WorldUtil.getMinSection(world);
|
|
|
|
- final int minBlockX = net.minecraft.util.Mth.floor(aabb.minX - COLLISION_EPSILON) - 1;
|
|
- final int maxBlockX = net.minecraft.util.Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1;
|
|
+ final int minBlockX = Mth.floor(aabb.minX - COLLISION_EPSILON) - 1;
|
|
+ final int maxBlockX = Mth.floor(aabb.maxX + COLLISION_EPSILON) + 1;
|
|
|
|
- final int minBlockY = Math.max((minSection << 4) - 1, net.minecraft.util.Mth.floor(aabb.minY - COLLISION_EPSILON) - 1);
|
|
- final int maxBlockY = Math.min((((ca.spottedleaf.moonrise.patches.collisions.world.CollisionLevel)world).moonrise$getMaxSection() << 4) + 16, net.minecraft.util.Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1);
|
|
+ final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - COLLISION_EPSILON) - 1);
|
|
+ final int maxBlockY = Math.min((WorldUtil.getMaxSection(world) << 4) + 16, Mth.floor(aabb.maxY + COLLISION_EPSILON) + 1);
|
|
|
|
- final int minBlockZ = net.minecraft.util.Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1;
|
|
- final int maxBlockZ = net.minecraft.util.Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1;
|
|
+ final int minBlockZ = Mth.floor(aabb.minZ - COLLISION_EPSILON) - 1;
|
|
+ final int maxBlockZ = Mth.floor(aabb.maxZ + COLLISION_EPSILON) + 1;
|
|
|
|
- final net.minecraft.core.BlockPos.MutableBlockPos mutablePos = new net.minecraft.core.BlockPos.MutableBlockPos();
|
|
- final net.minecraft.world.phys.shapes.CollisionContext collisionShape = new ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.LazyEntityCollisionContext(entity);
|
|
+ final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
|
+ final CollisionContext collisionShape = new LazyEntityCollisionContext(entity);
|
|
|
|
// special cases:
|
|
if (minBlockY > maxBlockY) {
|
|
@@ -1624,11 +1960,11 @@ public final class CollisionUtil {
|
|
final int maxChunkZ = maxBlockZ >> 4;
|
|
|
|
final boolean loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0;
|
|
- final net.minecraft.world.level.chunk.ChunkSource chunkSource = world.getChunkSource();
|
|
+ final ChunkSource chunkSource = world.getChunkSource();
|
|
|
|
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
|
for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
|
- final net.minecraft.world.level.chunk.ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, loadChunks);
|
|
+ final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, loadChunks);
|
|
|
|
if (chunk == null) {
|
|
if ((collisionFlags & COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS) != 0) {
|
|
@@ -1642,7 +1978,7 @@ public final class CollisionUtil {
|
|
continue;
|
|
}
|
|
|
|
- final net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunk.getSections();
|
|
+ final LevelChunkSection[] sections = chunk.getSections();
|
|
|
|
// bound y
|
|
for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
|
@@ -1650,16 +1986,16 @@ public final class CollisionUtil {
|
|
if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
|
continue;
|
|
}
|
|
- final net.minecraft.world.level.chunk.LevelChunkSection section = sections[sectionIdx];
|
|
- if (section == null || section.hasOnlyAir()) {
|
|
+ final LevelChunkSection section = sections[sectionIdx];
|
|
+ if (section.hasOnlyAir()) {
|
|
// empty
|
|
continue;
|
|
}
|
|
|
|
- final boolean hasSpecial = ((ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection)section).moonrise$getSpecialCollidingBlocks() != 0;
|
|
+ final boolean hasSpecial = ((BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks();
|
|
final int sectionAdjust = !hasSpecial ? 1 : 0;
|
|
|
|
- final net.minecraft.world.level.chunk.PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.states;
|
|
+ final PalettedContainer<BlockState> blocks = section.states;
|
|
|
|
final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) + sectionAdjust : 0;
|
|
final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) - sectionAdjust : 15;
|
|
@@ -1683,21 +2019,21 @@ public final class CollisionUtil {
|
|
continue;
|
|
}
|
|
|
|
- final net.minecraft.world.level.block.state.BlockState blockData = blocks.get(localBlockIndex);
|
|
+ final BlockState blockData = blocks.get(localBlockIndex);
|
|
|
|
- if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)blockData).moonrise$emptyCollisionShape()) {
|
|
+ if (((CollisionBlockState)blockData).moonrise$emptyContextCollisionShape()) {
|
|
continue;
|
|
}
|
|
|
|
- net.minecraft.world.phys.shapes.VoxelShape blockCollision = ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)blockData).moonrise$getConstantCollisionShape();
|
|
+ VoxelShape blockCollision = ((CollisionBlockState)blockData).moonrise$getConstantContextCollisionShape();
|
|
|
|
- if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == net.minecraft.world.level.block.Blocks.MOVING_PISTON))) {
|
|
+ if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) {
|
|
if (blockCollision == null) {
|
|
mutablePos.set(blockX, blockY, blockZ);
|
|
blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape);
|
|
}
|
|
|
|
- net.minecraft.world.phys.AABB singleAABB = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
|
|
+ AABB singleAABB = ((CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
|
|
if (singleAABB != null) {
|
|
singleAABB = singleAABB.move((double)blockX, (double)blockY, (double)blockZ);
|
|
if (!voxelShapeIntersect(aabb, singleAABB)) {
|
|
@@ -1724,7 +2060,7 @@ public final class CollisionUtil {
|
|
continue;
|
|
}
|
|
|
|
- final net.minecraft.world.phys.shapes.VoxelShape blockCollisionOffset = blockCollision.move((double)blockX, (double)blockY, (double)blockZ);
|
|
+ final VoxelShape blockCollisionOffset = blockCollision.move((double)blockX, (double)blockY, (double)blockZ);
|
|
|
|
if (!voxelShapeIntersectNoEmpty(blockCollisionOffset, aabb)) {
|
|
continue;
|
|
@@ -1755,8 +2091,8 @@ public final class CollisionUtil {
|
|
return ret;
|
|
}
|
|
|
|
- public static boolean getEntityHardCollisions(final net.minecraft.world.level.Level world, final net.minecraft.world.entity.Entity entity, net.minecraft.world.phys.AABB aabb,
|
|
- final java.util.List<net.minecraft.world.phys.AABB> into, final int collisionFlags, final java.util.function.Predicate<net.minecraft.world.entity.Entity> predicate) {
|
|
+ public static boolean getEntityHardCollisions(final Level world, final Entity entity, AABB aabb,
|
|
+ final List<AABB> into, final int collisionFlags, final Predicate<Entity> predicate) {
|
|
final boolean checkOnly = (collisionFlags & COLLISION_FLAG_CHECK_ONLY) != 0;
|
|
|
|
boolean ret = false;
|
|
@@ -1765,15 +2101,15 @@ public final class CollisionUtil {
|
|
// Vanilla for hard collisions has this backwards, and they expand by +epsilon but this causes terrible problems
|
|
// specifically with boat collisions.
|
|
aabb = aabb.inflate(-COLLISION_EPSILON, -COLLISION_EPSILON, -COLLISION_EPSILON);
|
|
- final java.util.List<net.minecraft.world.entity.Entity> entities;
|
|
- if (entity != null && ((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)entity).moonrise$isHardColliding()) {
|
|
+ final List<Entity> entities;
|
|
+ if (entity != null && ((ChunkSystemEntity)entity).moonrise$isHardColliding()) {
|
|
entities = world.getEntities(entity, aabb, predicate);
|
|
} else {
|
|
- entities = ((ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter)world).moonrise$getHardCollidingEntities(entity, aabb, predicate);
|
|
+ entities = ((ChunkSystemEntityGetter)world).moonrise$getHardCollidingEntities(entity, aabb, predicate);
|
|
}
|
|
|
|
for (int i = 0, len = entities.size(); i < len; ++i) {
|
|
- final net.minecraft.world.entity.Entity otherEntity = entities.get(i);
|
|
+ final Entity otherEntity = entities.get(i);
|
|
|
|
if (otherEntity.isSpectator()) {
|
|
continue;
|
|
@@ -1792,10 +2128,10 @@ public final class CollisionUtil {
|
|
return ret;
|
|
}
|
|
|
|
- public static boolean getCollisions(final net.minecraft.world.level.Level world, final net.minecraft.world.entity.Entity entity, final net.minecraft.world.phys.AABB aabb,
|
|
- final java.util.List<net.minecraft.world.phys.shapes.VoxelShape> intoVoxel, final java.util.List<net.minecraft.world.phys.AABB> intoAABB, final int collisionFlags,
|
|
- final java.util.function.BiPredicate<net.minecraft.world.level.block.state.BlockState, net.minecraft.core.BlockPos> blockPredicate,
|
|
- final java.util.function.Predicate<net.minecraft.world.entity.Entity> entityPredicate) {
|
|
+ public static boolean getCollisions(final Level world, final Entity entity, final AABB aabb,
|
|
+ final List<VoxelShape> intoVoxel, final List<AABB> intoAABB, final int collisionFlags,
|
|
+ final BiPredicate<BlockState, BlockPos> blockPredicate,
|
|
+ final Predicate<Entity> entityPredicate) {
|
|
if ((collisionFlags & COLLISION_FLAG_CHECK_ONLY) != 0) {
|
|
return getCollisionsForBlocksOrWorldBorder(world, entity, aabb, intoVoxel, intoAABB, collisionFlags, blockPredicate)
|
|
|| getEntityHardCollisions(world, entity, aabb, intoAABB, collisionFlags, entityPredicate);
|
|
@@ -1805,12 +2141,12 @@ public final class CollisionUtil {
|
|
}
|
|
}
|
|
|
|
- public static final class LazyEntityCollisionContext extends net.minecraft.world.phys.shapes.EntityCollisionContext {
|
|
+ public static final class LazyEntityCollisionContext extends EntityCollisionContext {
|
|
|
|
- private net.minecraft.world.phys.shapes.CollisionContext delegate;
|
|
+ private CollisionContext delegate;
|
|
private boolean delegated;
|
|
|
|
- public LazyEntityCollisionContext(final net.minecraft.world.entity.Entity entity) {
|
|
+ public LazyEntityCollisionContext(final Entity entity) {
|
|
super(false, 0.0, null, null, entity);
|
|
}
|
|
|
|
@@ -1820,10 +2156,10 @@ public final class CollisionUtil {
|
|
return delegated;
|
|
}
|
|
|
|
- public net.minecraft.world.phys.shapes.CollisionContext getDelegate() {
|
|
+ public CollisionContext getDelegate() {
|
|
this.delegated = true;
|
|
- final net.minecraft.world.entity.Entity entity = this.getEntity();
|
|
- return this.delegate == null ? this.delegate = (entity == null ? net.minecraft.world.phys.shapes.CollisionContext.empty() : net.minecraft.world.phys.shapes.CollisionContext.of(entity)) : this.delegate;
|
|
+ final Entity entity = this.getEntity();
|
|
+ return this.delegate == null ? this.delegate = (entity == null ? CollisionContext.empty() : CollisionContext.of(entity)) : this.delegate;
|
|
}
|
|
|
|
@Override
|
|
@@ -1832,17 +2168,17 @@ public final class CollisionUtil {
|
|
}
|
|
|
|
@Override
|
|
- public boolean isAbove(final net.minecraft.world.phys.shapes.VoxelShape shape, final net.minecraft.core.BlockPos pos, final boolean defaultValue) {
|
|
+ public boolean isAbove(final VoxelShape shape, final BlockPos pos, final boolean defaultValue) {
|
|
return this.getDelegate().isAbove(shape, pos, defaultValue);
|
|
}
|
|
|
|
@Override
|
|
- public boolean isHoldingItem(final net.minecraft.world.item.Item item) {
|
|
+ public boolean isHoldingItem(final Item item) {
|
|
return this.getDelegate().isHoldingItem(item);
|
|
}
|
|
|
|
@Override
|
|
- public boolean canStandOnFluid(final net.minecraft.world.level.material.FluidState state, final net.minecraft.world.level.material.FluidState fluidState) {
|
|
+ public boolean canStandOnFluid(final FluidState state, final FluidState fluidState) {
|
|
return this.getDelegate().canStandOnFluid(state, fluidState);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/ExplosionBlockCache.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/ExplosionBlockCache.java
|
|
index eb7200657d5c7ac37ee93868ba43be0aefecac6d..35c8aaf0bfa42717f45eed1d1072e1614874de91 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/ExplosionBlockCache.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/ExplosionBlockCache.java
|
|
@@ -1,18 +1,23 @@
|
|
package ca.spottedleaf.moonrise.patches.collisions;
|
|
|
|
+import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.material.FluidState;
|
|
+import net.minecraft.world.phys.shapes.VoxelShape;
|
|
+
|
|
public final class ExplosionBlockCache {
|
|
|
|
public final long key;
|
|
- public final net.minecraft.core.BlockPos immutablePos;
|
|
- public final net.minecraft.world.level.block.state.BlockState blockState;
|
|
- public final net.minecraft.world.level.material.FluidState fluidState;
|
|
+ public final BlockPos immutablePos;
|
|
+ public final BlockState blockState;
|
|
+ public final FluidState fluidState;
|
|
public final float resistance;
|
|
public final boolean outOfWorld;
|
|
public Boolean shouldExplode; // null -> not called yet
|
|
- public net.minecraft.world.phys.shapes.VoxelShape cachedCollisionShape;
|
|
+ public VoxelShape cachedCollisionShape;
|
|
|
|
- public ExplosionBlockCache(final long key, final net.minecraft.core.BlockPos immutablePos, final net.minecraft.world.level.block.state.BlockState blockState,
|
|
- final net.minecraft.world.level.material.FluidState fluidState, final float resistance, final boolean outOfWorld) {
|
|
+ public ExplosionBlockCache(final long key, final BlockPos immutablePos, final BlockState blockState,
|
|
+ final FluidState fluidState, final float resistance, final boolean outOfWorld) {
|
|
this.key = key;
|
|
this.immutablePos = immutablePos;
|
|
this.blockState = blockState;
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/block/CollisionBlockState.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/block/CollisionBlockState.java
|
|
index 02b29c563a298e06186de010de68a716bccba494..a38ab583200ebf68ca68fdddf2d12077720b72b7 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/block/CollisionBlockState.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/block/CollisionBlockState.java
|
|
@@ -1,5 +1,7 @@
|
|
package ca.spottedleaf.moonrise.patches.collisions.block;
|
|
|
|
+import net.minecraft.world.phys.shapes.VoxelShape;
|
|
+
|
|
public interface CollisionBlockState {
|
|
|
|
// note: this does not consider canOcclude, it is only based on the cached collision shape (i.e hasCache())
|
|
@@ -9,6 +11,9 @@ public interface CollisionBlockState {
|
|
// whether the cached collision shape exists and is empty
|
|
public boolean moonrise$emptyCollisionShape();
|
|
|
|
+ // whether the context-sensitive shape is constant and is empty
|
|
+ public boolean moonrise$emptyContextCollisionShape();
|
|
+
|
|
// indicates that occludesFullBlock is cached for the collision shape
|
|
public boolean moonrise$hasCache();
|
|
|
|
@@ -20,7 +25,5 @@ public interface CollisionBlockState {
|
|
// value is still unique
|
|
public int moonrise$uniqueId2();
|
|
|
|
- public net.minecraft.world.phys.shapes.VoxelShape moonrise$getConstantCollisionShape();
|
|
-
|
|
- public net.minecraft.world.phys.AABB moonrise$getConstantCollisionAABB();
|
|
+ public VoxelShape moonrise$getConstantContextCollisionShape();
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CachedToAABBs.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CachedToAABBs.java
|
|
index 5fe1dad9dad368911aedbe6ba7fcd8f9b0189d32..9d33ead3a97d86b371e4d9ad9fed80d789bed844 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CachedToAABBs.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CachedToAABBs.java
|
|
@@ -1,27 +1,31 @@
|
|
package ca.spottedleaf.moonrise.patches.collisions.shape;
|
|
|
|
+import net.minecraft.world.phys.AABB;
|
|
+import java.util.ArrayList;
|
|
+import java.util.List;
|
|
+
|
|
public record CachedToAABBs(
|
|
- java.util.List<net.minecraft.world.phys.AABB> aabbs,
|
|
+ List<AABB> aabbs,
|
|
boolean isOffset,
|
|
double offX, double offY, double offZ
|
|
) {
|
|
|
|
- public ca.spottedleaf.moonrise.patches.collisions.shape.CachedToAABBs removeOffset() {
|
|
- final java.util.List<net.minecraft.world.phys.AABB> toOffset = this.aabbs;
|
|
+ public CachedToAABBs removeOffset() {
|
|
+ final List<AABB> toOffset = this.aabbs;
|
|
final double offX = this.offX;
|
|
final double offY = this.offY;
|
|
final double offZ = this.offZ;
|
|
|
|
- final java.util.List<net.minecraft.world.phys.AABB> ret = new java.util.ArrayList<>(toOffset.size());
|
|
+ final List<AABB> ret = new ArrayList<>(toOffset.size());
|
|
|
|
for (int i = 0, len = toOffset.size(); i < len; ++i) {
|
|
ret.add(toOffset.get(i).move(offX, offY, offZ));
|
|
}
|
|
|
|
- return new ca.spottedleaf.moonrise.patches.collisions.shape.CachedToAABBs(ret, false, 0.0, 0.0, 0.0);
|
|
+ return new CachedToAABBs(ret, false, 0.0, 0.0, 0.0);
|
|
}
|
|
|
|
- public static ca.spottedleaf.moonrise.patches.collisions.shape.CachedToAABBs offset(final ca.spottedleaf.moonrise.patches.collisions.shape.CachedToAABBs cache, final double offX, final double offY, final double offZ) {
|
|
+ public static CachedToAABBs offset(final CachedToAABBs cache, final double offX, final double offY, final double offZ) {
|
|
if (offX == 0.0 && offY == 0.0 && offZ == 0.0) {
|
|
return cache;
|
|
}
|
|
@@ -30,6 +34,6 @@ public record CachedToAABBs(
|
|
final double resY = cache.offY + offY;
|
|
final double resZ = cache.offZ + offZ;
|
|
|
|
- return new ca.spottedleaf.moonrise.patches.collisions.shape.CachedToAABBs(cache.aabbs, true, resX, resY, resZ);
|
|
+ return new CachedToAABBs(cache.aabbs, true, resX, resY, resZ);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionDiscreteVoxelShape.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionDiscreteVoxelShape.java
|
|
index a09efadea9b733840bbe69830dd8f2a303fe656f..07fe5e02c2d0a27d2fe37bb45761654dc2d02e5d 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionDiscreteVoxelShape.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionDiscreteVoxelShape.java
|
|
@@ -2,6 +2,6 @@ package ca.spottedleaf.moonrise.patches.collisions.shape;
|
|
|
|
public interface CollisionDiscreteVoxelShape {
|
|
|
|
- public ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData moonrise$getOrCreateCachedShapeData();
|
|
+ public CachedShapeData moonrise$getOrCreateCachedShapeData();
|
|
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionVoxelShape.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionVoxelShape.java
|
|
index 70371eb87c11a106e8513cdbc8d938dda088f745..05d7b3f9d8659c259f3ed0537c57e6e43eb6e288 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionVoxelShape.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionVoxelShape.java
|
|
@@ -1,5 +1,9 @@
|
|
package ca.spottedleaf.moonrise.patches.collisions.shape;
|
|
|
|
+import net.minecraft.core.Direction;
|
|
+import net.minecraft.world.phys.AABB;
|
|
+import net.minecraft.world.phys.shapes.VoxelShape;
|
|
+
|
|
public interface CollisionVoxelShape {
|
|
|
|
public double moonrise$offsetX();
|
|
@@ -14,16 +18,16 @@ public interface CollisionVoxelShape {
|
|
|
|
public double[] moonrise$rootCoordinatesZ();
|
|
|
|
- public ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData moonrise$getCachedVoxelData();
|
|
+ public CachedShapeData moonrise$getCachedVoxelData();
|
|
|
|
// rets null if not possible to represent this shape as one AABB
|
|
- public net.minecraft.world.phys.AABB moonrise$getSingleAABBRepresentation();
|
|
+ public AABB moonrise$getSingleAABBRepresentation();
|
|
|
|
// ONLY USE INTERNALLY, ONLY FOR INITIALISING IN CONSTRUCTOR: VOXELSHAPES ARE STATIC
|
|
public void moonrise$initCache();
|
|
|
|
// this returns empty if not clamped to 1.0 or 0.0 depending on direction
|
|
- public net.minecraft.world.phys.shapes.VoxelShape moonrise$getFaceShapeClamped(final net.minecraft.core.Direction direction);
|
|
+ public VoxelShape moonrise$getFaceShapeClamped(final Direction direction);
|
|
|
|
public boolean moonrise$isFullBlock();
|
|
|
|
@@ -32,5 +36,5 @@ public interface CollisionVoxelShape {
|
|
public boolean moonrise$occludesFullBlockIfCached();
|
|
|
|
// uses a cache internally
|
|
- public net.minecraft.world.phys.shapes.VoxelShape moonrise$orUnoptimized(final net.minecraft.world.phys.shapes.VoxelShape other);
|
|
+ public VoxelShape moonrise$orUnoptimized(final VoxelShape other);
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/MergedORCache.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/MergedORCache.java
|
|
index 4217426d3eca5e5cd2bc37e509f84da1d6fed0b2..44831fc18efb7534dc6e4822f3c9b5cdc4dcc33e 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/MergedORCache.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/MergedORCache.java
|
|
@@ -1,8 +1,10 @@
|
|
package ca.spottedleaf.moonrise.patches.collisions.shape;
|
|
|
|
+import net.minecraft.world.phys.shapes.VoxelShape;
|
|
+
|
|
public record MergedORCache(
|
|
- net.minecraft.world.phys.shapes.VoxelShape key,
|
|
- net.minecraft.world.phys.shapes.VoxelShape result
|
|
+ VoxelShape key,
|
|
+ VoxelShape result
|
|
) {
|
|
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/util/EmptyStreamForMoveCall.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/util/EmptyStreamForMoveCall.java
|
|
deleted file mode 100644
|
|
index 673103f160cbe577c6e05f998706af4e6850011b..0000000000000000000000000000000000000000
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/util/EmptyStreamForMoveCall.java
|
|
+++ /dev/null
|
|
@@ -1,225 +0,0 @@
|
|
-package ca.spottedleaf.moonrise.patches.collisions.util;
|
|
-
|
|
-import java.util.Iterator;
|
|
-import java.util.Optional;
|
|
-import java.util.Spliterator;
|
|
-import java.util.stream.Stream;
|
|
-
|
|
-public final class EmptyStreamForMoveCall<T> implements java.util.stream.Stream<T> {
|
|
-
|
|
- public static final ca.spottedleaf.moonrise.patches.collisions.util.EmptyStreamForMoveCall INSTANCE = new ca.spottedleaf.moonrise.patches.collisions.util.EmptyStreamForMoveCall();
|
|
-
|
|
- @Override
|
|
- public boolean noneMatch(java.util.function.Predicate<? super T> predicate) {
|
|
- return false; // important: ret false so the branch is never taken by mojang code
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.Stream<T> filter(java.util.function.Predicate<? super T> predicate) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public <R> java.util.stream.Stream<R> map(java.util.function.Function<? super T, ? extends R> mapper) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.IntStream mapToInt(java.util.function.ToIntFunction<? super T> mapper) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.LongStream mapToLong(java.util.function.ToLongFunction<? super T> mapper) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.DoubleStream mapToDouble(java.util.function.ToDoubleFunction<? super T> mapper) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public <R> java.util.stream.Stream<R> flatMap(java.util.function.Function<? super T, ? extends java.util.stream.Stream<? extends R>> mapper) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.IntStream flatMapToInt(java.util.function.Function<? super T, ? extends java.util.stream.IntStream> mapper) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.LongStream flatMapToLong(java.util.function.Function<? super T, ? extends java.util.stream.LongStream> mapper) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.DoubleStream flatMapToDouble(java.util.function.Function<? super T, ? extends java.util.stream.DoubleStream> mapper) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.Stream<T> distinct() {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.Stream<T> sorted() {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.Stream<T> sorted(java.util.Comparator<? super T> comparator) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.Stream<T> peek(java.util.function.Consumer<? super T> action) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.Stream<T> limit(long maxSize) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public java.util.stream.Stream<T> skip(long n) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void forEach(java.util.function.Consumer<? super T> action) {
|
|
-
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void forEachOrdered(java.util.function.Consumer<? super T> action) {
|
|
-
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Object[] toArray() {
|
|
- return new Object[0];
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public <A> A[] toArray(java.util.function.IntFunction<A[]> generator) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public T reduce(T identity, java.util.function.BinaryOperator<T> accumulator) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Optional<T> reduce(java.util.function.BinaryOperator<T> accumulator) {
|
|
- return java.util.Optional.empty();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public <U> U reduce(U identity, java.util.function.BiFunction<U, ? super T, U> accumulator, java.util.function.BinaryOperator<U> combiner) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public <R> R collect(java.util.function.Supplier<R> supplier, java.util.function.BiConsumer<R, ? super T> accumulator, java.util.function.BiConsumer<R, R> combiner) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public <R, A> R collect(java.util.stream.Collector<? super T, A, R> collector) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Optional<T> min(java.util.Comparator<? super T> comparator) {
|
|
- return java.util.Optional.empty();
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Optional<T> max(java.util.Comparator<? super T> comparator) {
|
|
- return java.util.Optional.empty();
|
|
- }
|
|
-
|
|
- @Override
|
|
- public long count() {
|
|
- return 0;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean anyMatch(java.util.function.Predicate<? super T> predicate) {
|
|
- return false;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean allMatch(java.util.function.Predicate<? super T> predicate) {
|
|
- return false;
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Optional<T> findFirst() {
|
|
- return java.util.Optional.empty();
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Optional<T> findAny() {
|
|
- return java.util.Optional.empty();
|
|
- }
|
|
-
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Iterator<T> iterator() {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Spliterator<T> spliterator() {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public boolean isParallel() {
|
|
- return false;
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Stream<T> sequential() {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Stream<T> parallel() {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Stream<T> unordered() {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @org.jetbrains.annotations.NotNull
|
|
- @Override
|
|
- public Stream<T> onClose(Runnable closeHandler) {
|
|
- return null;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public void close() {
|
|
-
|
|
- }
|
|
-}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/util/FluidOcclusionCacheKey.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/util/FluidOcclusionCacheKey.java
|
|
index 128267ff40b38c7b3ea0feb5133825cc6aae075b..cf9ffdeff6bf0b62a45f7a44dbfe0dd7d17dc4f4 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/util/FluidOcclusionCacheKey.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/util/FluidOcclusionCacheKey.java
|
|
@@ -1,4 +1,7 @@
|
|
package ca.spottedleaf.moonrise.patches.collisions.util;
|
|
|
|
-public record FluidOcclusionCacheKey(net.minecraft.world.level.block.state.BlockState first, net.minecraft.world.level.block.state.BlockState second, net.minecraft.core.Direction direction, boolean result) {
|
|
+import net.minecraft.core.Direction;
|
|
+import net.minecraft.world.level.block.state.BlockState;
|
|
+
|
|
+public record FluidOcclusionCacheKey(BlockState first, BlockState second, Direction direction, boolean result) {
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/CollisionLevel.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/CollisionLevel.java
|
|
deleted file mode 100644
|
|
index e851e81e13edbad6316df63fcb7095d48f85c5b0..0000000000000000000000000000000000000000
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/CollisionLevel.java
|
|
+++ /dev/null
|
|
@@ -1,9 +0,0 @@
|
|
-package ca.spottedleaf.moonrise.patches.collisions.world;
|
|
-
|
|
-public interface CollisionLevel {
|
|
-
|
|
- public int moonrise$getMinSection();
|
|
-
|
|
- public int moonrise$getMaxSection();
|
|
-
|
|
-}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerTrackedEntity.java b/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerTrackedEntity.java
|
|
index 1fa07bef57d82c6d5242aaaf66011f0913515231..8e7472157a98de607c03769a91f64c8369fd3ea6 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerTrackedEntity.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/entity_tracker/EntityTrackerTrackedEntity.java
|
|
@@ -10,4 +10,6 @@ public interface EntityTrackerTrackedEntity {
|
|
|
|
public void moonrise$clearPlayers();
|
|
|
|
+ public boolean moonrise$hasPlayers();
|
|
+
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/fast_palette/FastPalette.java b/src/main/java/ca/spottedleaf/moonrise/patches/fast_palette/FastPalette.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4a7abd239a9c59aa98947e7993962d75e9051902
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/fast_palette/FastPalette.java
|
|
@@ -0,0 +1,9 @@
|
|
+package ca.spottedleaf.moonrise.patches.fast_palette;
|
|
+
|
|
+public interface FastPalette<T> {
|
|
+
|
|
+ public default T[] moonrise$getRawPalette(final FastPaletteData<T> src) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/fast_palette/FastPaletteData.java b/src/main/java/ca/spottedleaf/moonrise/patches/fast_palette/FastPaletteData.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4503f3495846a7d7ed082b9e24636044e4fbccd1
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/fast_palette/FastPaletteData.java
|
|
@@ -0,0 +1,9 @@
|
|
+package ca.spottedleaf.moonrise.patches.fast_palette;
|
|
+
|
|
+public interface FastPaletteData<T> {
|
|
+
|
|
+ public T[] moonrise$getPalette();
|
|
+
|
|
+ public void moonrise$setPalette(final T[] palette);
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/fluid/FluidFluidState.java b/src/main/java/ca/spottedleaf/moonrise/patches/fluid/FluidFluidState.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..107c97089354edd35f330582f5e0c8a18e792a6e
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/fluid/FluidFluidState.java
|
|
@@ -0,0 +1,5 @@
|
|
+package ca.spottedleaf.moonrise.patches.fluid;
|
|
+
|
|
+public interface FluidFluidState {
|
|
+ public void moonrise$initCaches();
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_getblock/GetBlockChunk.java b/src/main/java/ca/spottedleaf/moonrise/patches/getblock/GetBlockChunk.java
|
|
similarity index 75%
|
|
rename from src/main/java/ca/spottedleaf/moonrise/patches/chunk_getblock/GetBlockChunk.java
|
|
rename to src/main/java/ca/spottedleaf/moonrise/patches/getblock/GetBlockChunk.java
|
|
index 08338917dc61c856eaba0b76e05c1497c458399d..540c14a6d2c216cd3ef2a9c4056e15712bf8cb8c 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_getblock/GetBlockChunk.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/getblock/GetBlockChunk.java
|
|
@@ -1,4 +1,4 @@
|
|
-package ca.spottedleaf.moonrise.patches.chunk_getblock;
|
|
+package ca.spottedleaf.moonrise.patches.getblock;
|
|
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/blockstate/StarlightAbstractBlockState.java b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/blockstate/StarlightAbstractBlockState.java
|
|
index 2bfdf3721db9a45e36538d71cbefcb1d339e6c58..8e6d79b7c10ef25f5478b72c53c555423d615a2f 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/blockstate/StarlightAbstractBlockState.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/blockstate/StarlightAbstractBlockState.java
|
|
@@ -4,6 +4,4 @@ public interface StarlightAbstractBlockState {
|
|
|
|
public boolean starlight$isConditionallyFullOpaque();
|
|
|
|
- public int starlight$getOpacityIfCached();
|
|
-
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/BlockStarLightEngine.java b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/BlockStarLightEngine.java
|
|
index 154443ac1ee1d6d18b8ff0f40a307d638b213aeb..fa7b784a89626e8528c249d7889a598bd7ee3d49 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/BlockStarLightEngine.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/BlockStarLightEngine.java
|
|
@@ -1,8 +1,10 @@
|
|
package ca.spottedleaf.moonrise.patches.starlight.light;
|
|
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import ca.spottedleaf.moonrise.patches.starlight.blockstate.StarlightAbstractBlockState;
|
|
import ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk;
|
|
import net.minecraft.core.BlockPos;
|
|
+import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
@@ -91,7 +93,7 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|
|
|
final int currentLevel = this.getLightLevel(worldX, worldY, worldZ);
|
|
final BlockState blockState = this.getBlockState(worldX, worldY, worldZ);
|
|
- final int emittedLevel = blockState.getLightEmission() & emittedMask;
|
|
+ final int emittedLevel = (PlatformHooks.get().getLightEmission(blockState, lightAccess.getLevel(), this.lightEmissionPos.set(worldX, worldY, worldZ))) & emittedMask;
|
|
|
|
this.setLightLevel(worldX, worldY, worldZ, emittedLevel);
|
|
// this accounts for change in emitted light that would cause an increase
|
|
@@ -119,37 +121,32 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|
}
|
|
|
|
protected final BlockPos.MutableBlockPos recalcCenterPos = new BlockPos.MutableBlockPos();
|
|
- protected final BlockPos.MutableBlockPos recalcNeighbourPos = new BlockPos.MutableBlockPos();
|
|
|
|
@Override
|
|
protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ,
|
|
final int expect) {
|
|
+ this.recalcCenterPos.set(worldX, worldY, worldZ);
|
|
+
|
|
final BlockState centerState = this.getBlockState(worldX, worldY, worldZ);
|
|
- int level = centerState.getLightEmission() & 0xF;
|
|
+ final BlockGetter world = lightAccess.getLevel();
|
|
+ int level = (PlatformHooks.get().getLightEmission(centerState, world, this.recalcCenterPos)) & this.emittedLightMask;
|
|
|
|
if (level >= (15 - 1) || level > expect) {
|
|
return level;
|
|
}
|
|
|
|
- final int sectionOffset = this.chunkSectionIndexOffset;
|
|
- final BlockState conditionallyOpaqueState;
|
|
- int opacity = ((StarlightAbstractBlockState)centerState).starlight$getOpacityIfCached();
|
|
-
|
|
- if (opacity == -1) {
|
|
- this.recalcCenterPos.set(worldX, worldY, worldZ);
|
|
- opacity = centerState.getLightBlock(lightAccess.getLevel(), this.recalcCenterPos);
|
|
- if (((StarlightAbstractBlockState)centerState).starlight$isConditionallyFullOpaque()) {
|
|
- conditionallyOpaqueState = centerState;
|
|
- } else {
|
|
- conditionallyOpaqueState = null;
|
|
- }
|
|
- } else if (opacity >= 15) {
|
|
+ final int opacity = Math.max(1, centerState.getLightBlock());
|
|
+ if (opacity >= 15) {
|
|
return level;
|
|
+ }
|
|
+ final BlockState conditionallyOpaqueState;
|
|
+ if (((StarlightAbstractBlockState)centerState).starlight$isConditionallyFullOpaque()) {
|
|
+ conditionallyOpaqueState = centerState;
|
|
} else {
|
|
conditionallyOpaqueState = null;
|
|
}
|
|
- opacity = Math.max(1, opacity);
|
|
|
|
+ final int sectionOffset = this.chunkSectionIndexOffset;
|
|
for (final AxisDirection direction : AXIS_DIRECTIONS) {
|
|
final int offX = worldX + direction.x;
|
|
final int offY = worldY + direction.y;
|
|
@@ -169,9 +166,8 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|
// here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that
|
|
// we don't read the blockstate because most of the time this is false, so using the faster
|
|
// known transparency lookup results in a net win
|
|
- this.recalcNeighbourPos.set(offX, offY, offZ);
|
|
- final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcNeighbourPos, direction.opposite.nms);
|
|
- final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcCenterPos, direction.nms);
|
|
+ final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(direction.opposite.nms);
|
|
+ final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(direction.nms);
|
|
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
|
|
// not allowed to propagate
|
|
continue;
|
|
@@ -205,30 +201,34 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|
final int offX = chunk.getPos().x << 4;
|
|
final int offZ = chunk.getPos().z << 4;
|
|
|
|
+ final PlatformHooks platformHooks = PlatformHooks.get();
|
|
+
|
|
+ final BlockGetter world = lightAccess.getLevel();
|
|
final LevelChunkSection[] sections = chunk.getSections();
|
|
for (int sectionY = this.minSection; sectionY <= this.maxSection; ++sectionY) {
|
|
final LevelChunkSection section = sections[sectionY - this.minSection];
|
|
- if (section == null || section.hasOnlyAir()) {
|
|
+ if (section.hasOnlyAir()) {
|
|
// no sources in empty sections
|
|
continue;
|
|
}
|
|
- if (!section.maybeHas((final BlockState state) -> {
|
|
- return state.getLightEmission() > 0;
|
|
- })) {
|
|
+ if (!section.maybeHas(platformHooks.maybeHasLightEmission())) {
|
|
// no light sources in palette
|
|
continue;
|
|
}
|
|
final PalettedContainer<BlockState> states = section.states;
|
|
final int offY = sectionY << 4;
|
|
|
|
+ final BlockPos.MutableBlockPos mutablePos = this.lightEmissionPos;
|
|
for (int index = 0; index < (16 * 16 * 16); ++index) {
|
|
final BlockState state = states.get(index);
|
|
- if (state.getLightEmission() <= 0) {
|
|
+ mutablePos.set(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15));
|
|
+
|
|
+ if ((platformHooks.getLightEmission(state, world, mutablePos)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
// index = x | (z << 4) | (y << 8)
|
|
- sources.add(new BlockPos(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15)));
|
|
+ sources.add(mutablePos.immutable());
|
|
}
|
|
}
|
|
|
|
@@ -238,12 +238,15 @@ public final class BlockStarLightEngine extends StarLightEngine {
|
|
@Override
|
|
public void lightChunk(final LightChunkGetter lightAccess, final ChunkAccess chunk, final boolean needsEdgeChecks) {
|
|
// setup sources
|
|
+ final BlockGetter world = lightAccess.getLevel();
|
|
+ final PlatformHooks platformHooks = PlatformHooks.get();
|
|
+
|
|
final int emittedMask = this.emittedLightMask;
|
|
final List<BlockPos> positions = this.getSources(lightAccess, chunk);
|
|
for (int i = 0, len = positions.size(); i < len; ++i) {
|
|
final BlockPos pos = positions.get(i);
|
|
final BlockState blockState = this.getBlockState(pos.getX(), pos.getY(), pos.getZ());
|
|
- final int emittedLight = blockState.getLightEmission() & emittedMask;
|
|
+ final int emittedLight = platformHooks.getLightEmission(blockState, world, pos) & emittedMask;
|
|
|
|
if (emittedLight <= this.getLightLevel(pos.getX(), pos.getY(), pos.getZ())) {
|
|
// some other source is brighter
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/SkyStarLightEngine.java b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/SkyStarLightEngine.java
|
|
index fdbc015f498164c9d2c578cd84a73def568142a4..f9aef289e9a2d6f63c98c72c56ef32b8793f57f4 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/SkyStarLightEngine.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/SkyStarLightEngine.java
|
|
@@ -290,9 +290,6 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|
);
|
|
}
|
|
|
|
- protected final BlockPos.MutableBlockPos recalcCenterPos = new BlockPos.MutableBlockPos();
|
|
- protected final BlockPos.MutableBlockPos recalcNeighbourPos = new BlockPos.MutableBlockPos();
|
|
-
|
|
@Override
|
|
protected int calculateLightValue(final LightChunkGetter lightAccess, final int worldX, final int worldY, final int worldZ,
|
|
final int expect) {
|
|
@@ -302,20 +299,13 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|
|
|
final int sectionOffset = this.chunkSectionIndexOffset;
|
|
final BlockState centerState = this.getBlockState(worldX, worldY, worldZ);
|
|
- int opacity = ((StarlightAbstractBlockState)centerState).starlight$getOpacityIfCached();
|
|
|
|
final BlockState conditionallyOpaqueState;
|
|
- if (opacity < 0) {
|
|
- this.recalcCenterPos.set(worldX, worldY, worldZ);
|
|
- opacity = Math.max(1, centerState.getLightBlock(lightAccess.getLevel(), this.recalcCenterPos));
|
|
- if (((StarlightAbstractBlockState)centerState).starlight$isConditionallyFullOpaque()) {
|
|
- conditionallyOpaqueState = centerState;
|
|
- } else {
|
|
- conditionallyOpaqueState = null;
|
|
- }
|
|
+ final int opacity = Math.max(1, centerState.getLightBlock());
|
|
+ if (((StarlightAbstractBlockState)centerState).starlight$isConditionallyFullOpaque()) {
|
|
+ conditionallyOpaqueState = centerState;
|
|
} else {
|
|
conditionallyOpaqueState = null;
|
|
- opacity = Math.max(1, opacity);
|
|
}
|
|
|
|
int level = 0;
|
|
@@ -340,9 +330,8 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|
// here the block can be conditionally opaque (i.e light cannot propagate from it), so we need to test that
|
|
// we don't read the blockstate because most of the time this is false, so using the faster
|
|
// known transparency lookup results in a net win
|
|
- this.recalcNeighbourPos.set(offX, offY, offZ);
|
|
- final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcNeighbourPos, direction.opposite.nms);
|
|
- final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(lightAccess.getLevel(), this.recalcCenterPos, direction.nms);
|
|
+ final VoxelShape neighbourFace = neighbourState.getFaceOcclusionShape(direction.opposite.nms);
|
|
+ final VoxelShape thisFace = conditionallyOpaqueState == null ? Shapes.empty() : conditionallyOpaqueState.getFaceOcclusionShape(direction.nms);
|
|
if (Shapes.faceShapeOccludes(thisFace, neighbourFace)) {
|
|
// not allowed to propagate
|
|
continue;
|
|
@@ -610,7 +599,6 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|
// clobbering the light values will result in broken propagation)
|
|
protected final int tryPropagateSkylight(final BlockGetter world, final int worldX, int startY, final int worldZ,
|
|
final boolean extrudeInitialised, final boolean delayLightSet) {
|
|
- final BlockPos.MutableBlockPos mutablePos = this.mutablePos3;
|
|
final int encodeOffset = this.coordinateOffset;
|
|
final long propagateDirection = AxisDirection.POSITIVE_Y.everythingButThisDirection; // just don't check upwards.
|
|
|
|
@@ -632,8 +620,7 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|
|
|
final VoxelShape fromShape;
|
|
if (((StarlightAbstractBlockState)above).starlight$isConditionallyFullOpaque()) {
|
|
- this.mutablePos2.set(worldX, startY + 1, worldZ);
|
|
- fromShape = above.getFaceOcclusionShape(world, this.mutablePos2, AxisDirection.NEGATIVE_Y.nms);
|
|
+ fromShape = above.getFaceOcclusionShape(AxisDirection.NEGATIVE_Y.nms);
|
|
if (Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
|
|
// above wont let us propagate
|
|
break;
|
|
@@ -642,49 +629,32 @@ public final class SkyStarLightEngine extends StarLightEngine {
|
|
fromShape = Shapes.empty();
|
|
}
|
|
|
|
- final int opacityIfCached = ((StarlightAbstractBlockState)current).starlight$getOpacityIfCached();
|
|
// does light propagate from the top down?
|
|
- if (opacityIfCached != -1) {
|
|
- if (opacityIfCached != 0) {
|
|
- // we cannot propagate 15 through this
|
|
- break;
|
|
- }
|
|
- // most of the time it falls here.
|
|
- // add to propagate
|
|
- // light set delayed until we determine if this nibble section is null
|
|
- this.appendToIncreaseQueue(
|
|
- ((worldX + (worldZ << 6) + (startY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | (15L << (6 + 6 + 16)) // we know we're at full lit here
|
|
- | (propagateDirection << (6 + 6 + 16 + 4))
|
|
- );
|
|
- } else {
|
|
- mutablePos.set(worldX, startY, worldZ);
|
|
- long flags = 0L;
|
|
- if (((StarlightAbstractBlockState)current).starlight$isConditionallyFullOpaque()) {
|
|
- final VoxelShape cullingFace = current.getFaceOcclusionShape(world, mutablePos, AxisDirection.POSITIVE_Y.nms);
|
|
+ long flags = 0L;
|
|
+ if (((StarlightAbstractBlockState)current).starlight$isConditionallyFullOpaque()) {
|
|
+ final VoxelShape cullingFace = current.getFaceOcclusionShape(AxisDirection.POSITIVE_Y.nms);
|
|
|
|
- if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
|
- // can't propagate here, we're done on this column.
|
|
- break;
|
|
- }
|
|
- flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
- }
|
|
-
|
|
- final int opacity = current.getLightBlock(world, mutablePos);
|
|
- if (opacity > 0) {
|
|
- // let the queued value (if any) handle it from here.
|
|
+ if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
|
+ // can't propagate here, we're done on this column.
|
|
break;
|
|
}
|
|
+ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
+ }
|
|
|
|
- // light set delayed until we determine if this nibble section is null
|
|
- this.appendToIncreaseQueue(
|
|
- ((worldX + (worldZ << 6) + (startY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | (15L << (6 + 6 + 16)) // we know we're at full lit here
|
|
- | (propagateDirection << (6 + 6 + 16 + 4))
|
|
- | flags
|
|
- );
|
|
+ final int opacity = current.getLightBlock();
|
|
+ if (opacity > 0) {
|
|
+ // let the queued value (if any) handle it from here.
|
|
+ break;
|
|
}
|
|
|
|
+ // light set delayed until we determine if this nibble section is null
|
|
+ this.appendToIncreaseQueue(
|
|
+ ((worldX + (worldZ << 6) + (startY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
+ | (15L << (6 + 6 + 16)) // we know we're at full lit here
|
|
+ | (propagateDirection << (6 + 6 + 16 + 4))
|
|
+ | flags
|
|
+ );
|
|
+
|
|
above = current;
|
|
|
|
if (this.getNibbleFromCache(worldX >> 4, startY >> 4, worldZ >> 4) == null) {
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/StarLightEngine.java b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/StarLightEngine.java
|
|
index 382c9e445af0d6ad2428fc22d0f63017c58191e2..8aeb5fb87f94a35659347a09a638420699b52a6f 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/StarLightEngine.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/StarLightEngine.java
|
|
@@ -1,6 +1,7 @@
|
|
package ca.spottedleaf.moonrise.patches.starlight.light;
|
|
|
|
import ca.spottedleaf.concurrentutil.util.IntegerUtil;
|
|
+import ca.spottedleaf.moonrise.common.PlatformHooks;
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
import ca.spottedleaf.moonrise.patches.starlight.blockstate.StarlightAbstractBlockState;
|
|
@@ -43,9 +44,9 @@ public abstract class StarLightEngine {
|
|
protected static enum AxisDirection {
|
|
|
|
// Declaration order is important and relied upon. Do not change without modifying propagation code.
|
|
- POSITIVE_X(1, 0, 0), NEGATIVE_X(-1, 0, 0),
|
|
- POSITIVE_Z(0, 0, 1), NEGATIVE_Z(0, 0, -1),
|
|
- POSITIVE_Y(0, 1, 0), NEGATIVE_Y(0, -1, 0);
|
|
+ POSITIVE_X(1, 0, 0, Direction.EAST) , NEGATIVE_X(-1, 0, 0, Direction.WEST),
|
|
+ POSITIVE_Z(0, 0, 1, Direction.SOUTH), NEGATIVE_Z(0, 0, -1, Direction.NORTH),
|
|
+ POSITIVE_Y(0, 1, 0, Direction.UP) , NEGATIVE_Y(0, -1, 0, Direction.DOWN);
|
|
|
|
static {
|
|
POSITIVE_X.opposite = NEGATIVE_X; NEGATIVE_X.opposite = POSITIVE_X;
|
|
@@ -62,11 +63,11 @@ public abstract class StarLightEngine {
|
|
public final long everythingButThisDirection;
|
|
public final long everythingButTheOppositeDirection;
|
|
|
|
- AxisDirection(final int x, final int y, final int z) {
|
|
+ AxisDirection(final int x, final int y, final int z, final Direction nms) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
- this.nms = Direction.fromDelta(x, y, z);
|
|
+ this.nms = nms;
|
|
this.everythingButThisDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << this.ordinal()));
|
|
// positive is always even, negative is always odd. Flip the 1 bit to get the negative direction.
|
|
this.everythingButTheOppositeDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << (this.ordinal() ^ 1)));
|
|
@@ -106,9 +107,7 @@ public abstract class StarLightEngine {
|
|
// index = x + (z * 5)
|
|
protected final boolean[][] emptinessMapCache = new boolean[5 * 5][];
|
|
|
|
- protected final BlockPos.MutableBlockPos mutablePos1 = new BlockPos.MutableBlockPos();
|
|
- protected final BlockPos.MutableBlockPos mutablePos2 = new BlockPos.MutableBlockPos();
|
|
- protected final BlockPos.MutableBlockPos mutablePos3 = new BlockPos.MutableBlockPos();
|
|
+ protected final BlockPos.MutableBlockPos lightEmissionPos = new BlockPos.MutableBlockPos();
|
|
|
|
protected int encodeOffsetX;
|
|
protected int encodeOffsetY;
|
|
@@ -1150,69 +1149,46 @@ public abstract class StarLightEngine {
|
|
if (blockState == null) {
|
|
continue;
|
|
}
|
|
- final int opacityCached = ((StarlightAbstractBlockState)blockState).starlight$getOpacityIfCached();
|
|
- if (opacityCached != -1) {
|
|
- final int targetLevel = propagatedLightLevel - Math.max(1, opacityCached);
|
|
- if (targetLevel > currentLevel) {
|
|
- currentNibble.set(localIndex, targetLevel);
|
|
- this.postLightUpdate(offX, offY, offZ);
|
|
-
|
|
- if (targetLevel > 1) {
|
|
- if (queueLength >= queue.length) {
|
|
- queue = this.resizeIncreaseQueue();
|
|
- }
|
|
- queue[queueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
- | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4));
|
|
- continue;
|
|
- }
|
|
- }
|
|
- continue;
|
|
- } else {
|
|
- this.mutablePos1.set(offX, offY, offZ);
|
|
- long flags = 0;
|
|
- if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
|
- final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
|
|
+ long flags = 0;
|
|
+ if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
|
+ final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
|
|
|
|
- if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
|
|
- continue;
|
|
- }
|
|
- flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
- }
|
|
-
|
|
- final int opacity = blockState.getLightBlock(world, this.mutablePos1);
|
|
- final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
|
|
- if (targetLevel <= currentLevel) {
|
|
+ if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
|
|
continue;
|
|
}
|
|
+ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
+ }
|
|
|
|
- currentNibble.set(localIndex, targetLevel);
|
|
- this.postLightUpdate(offX, offY, offZ);
|
|
+ final int opacity = blockState.getLightBlock();
|
|
+ final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
|
|
+ if (targetLevel <= currentLevel) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- if (targetLevel > 1) {
|
|
- if (queueLength >= queue.length) {
|
|
- queue = this.resizeIncreaseQueue();
|
|
- }
|
|
- queue[queueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
- | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4))
|
|
- | (flags);
|
|
+ currentNibble.set(localIndex, targetLevel);
|
|
+ this.postLightUpdate(offX, offY, offZ);
|
|
+
|
|
+ if (targetLevel > 1) {
|
|
+ if (queueLength >= queue.length) {
|
|
+ queue = this.resizeIncreaseQueue();
|
|
}
|
|
- continue;
|
|
+ queue[queueLength++] =
|
|
+ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
+ | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
+ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4))
|
|
+ | (flags);
|
|
}
|
|
+ continue;
|
|
}
|
|
} else {
|
|
// we actually need to worry about our state here
|
|
final BlockState fromBlock = this.getBlockState(posX, posY, posZ);
|
|
- this.mutablePos2.set(posX, posY, posZ);
|
|
for (final AxisDirection propagate : checkDirections) {
|
|
final int offX = posX + propagate.x;
|
|
final int offY = posY + propagate.y;
|
|
final int offZ = posZ + propagate.z;
|
|
|
|
- final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(world, this.mutablePos2, propagate.nms) : Shapes.empty();
|
|
+ final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(propagate.nms) : Shapes.empty();
|
|
|
|
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
|
|
continue;
|
|
@@ -1232,58 +1208,36 @@ public abstract class StarLightEngine {
|
|
if (blockState == null) {
|
|
continue;
|
|
}
|
|
- final int opacityCached = ((StarlightAbstractBlockState)blockState).starlight$getOpacityIfCached();
|
|
- if (opacityCached != -1) {
|
|
- final int targetLevel = propagatedLightLevel - Math.max(1, opacityCached);
|
|
- if (targetLevel > currentLevel) {
|
|
- currentNibble.set(localIndex, targetLevel);
|
|
- this.postLightUpdate(offX, offY, offZ);
|
|
-
|
|
- if (targetLevel > 1) {
|
|
- if (queueLength >= queue.length) {
|
|
- queue = this.resizeIncreaseQueue();
|
|
- }
|
|
- queue[queueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
- | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4));
|
|
- continue;
|
|
- }
|
|
- }
|
|
- continue;
|
|
- } else {
|
|
- this.mutablePos1.set(offX, offY, offZ);
|
|
- long flags = 0;
|
|
- if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
|
- final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
|
|
-
|
|
- if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
|
- continue;
|
|
- }
|
|
- flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
- }
|
|
+ long flags = 0;
|
|
+ if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
|
+ final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
|
|
|
|
- final int opacity = blockState.getLightBlock(world, this.mutablePos1);
|
|
- final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
|
|
- if (targetLevel <= currentLevel) {
|
|
+ if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
|
continue;
|
|
}
|
|
+ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
+ }
|
|
|
|
- currentNibble.set(localIndex, targetLevel);
|
|
- this.postLightUpdate(offX, offY, offZ);
|
|
+ final int opacity = blockState.getLightBlock();
|
|
+ final int targetLevel = propagatedLightLevel - Math.max(1, opacity);
|
|
+ if (targetLevel <= currentLevel) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- if (targetLevel > 1) {
|
|
- if (queueLength >= queue.length) {
|
|
- queue = this.resizeIncreaseQueue();
|
|
- }
|
|
- queue[queueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
- | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4))
|
|
- | (flags);
|
|
+ currentNibble.set(localIndex, targetLevel);
|
|
+ this.postLightUpdate(offX, offY, offZ);
|
|
+
|
|
+ if (targetLevel > 1) {
|
|
+ if (queueLength >= queue.length) {
|
|
+ queue = this.resizeIncreaseQueue();
|
|
}
|
|
- continue;
|
|
+ queue[queueLength++] =
|
|
+ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
+ | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
+ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4))
|
|
+ | (flags);
|
|
}
|
|
+ continue;
|
|
}
|
|
}
|
|
}
|
|
@@ -1304,6 +1258,8 @@ public abstract class StarLightEngine {
|
|
final int sectionOffset = this.chunkSectionIndexOffset;
|
|
final int emittedMask = this.emittedLightMask;
|
|
|
|
+ final PlatformHooks platformHooks = PlatformHooks.get();
|
|
+
|
|
while (queueReadIndex < queueLength) {
|
|
final long queueValue = queue[queueReadIndex++];
|
|
|
|
@@ -1335,109 +1291,63 @@ public abstract class StarLightEngine {
|
|
if (blockState == null) {
|
|
continue;
|
|
}
|
|
- final int opacityCached = ((StarlightAbstractBlockState)blockState).starlight$getOpacityIfCached();
|
|
- if (opacityCached != -1) {
|
|
- final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacityCached));
|
|
- if (lightLevel > targetLevel) {
|
|
- // it looks like another source propagated here, so re-propagate it
|
|
- if (increaseQueueLength >= increaseQueue.length) {
|
|
- increaseQueue = this.resizeIncreaseQueue();
|
|
- }
|
|
- increaseQueue[increaseQueueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((lightLevel & 0xFL) << (6 + 6 + 16))
|
|
- | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
- | FLAG_RECHECK_LEVEL;
|
|
- continue;
|
|
- }
|
|
- final int emittedLight = blockState.getLightEmission() & emittedMask;
|
|
- if (emittedLight != 0) {
|
|
- // re-propagate source
|
|
- // note: do not set recheck level, or else the propagation will fail
|
|
- if (increaseQueueLength >= increaseQueue.length) {
|
|
- increaseQueue = this.resizeIncreaseQueue();
|
|
- }
|
|
- increaseQueue[increaseQueueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((emittedLight & 0xFL) << (6 + 6 + 16))
|
|
- | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
- | (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque() ? (FLAG_WRITE_LEVEL | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) : FLAG_WRITE_LEVEL);
|
|
- }
|
|
-
|
|
- currentNibble.set(localIndex, 0);
|
|
- this.postLightUpdate(offX, offY, offZ);
|
|
+ this.lightEmissionPos.set(offX, offY, offZ);
|
|
+ long flags = 0;
|
|
+ if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
|
+ final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
|
|
|
|
- if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour...
|
|
- if (queueLength >= queue.length) {
|
|
- queue = this.resizeDecreaseQueue();
|
|
- }
|
|
- queue[queueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
- | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4));
|
|
+ if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
|
|
continue;
|
|
}
|
|
- continue;
|
|
- } else {
|
|
- this.mutablePos1.set(offX, offY, offZ);
|
|
- long flags = 0;
|
|
- if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
|
- final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
|
|
-
|
|
- if (Shapes.faceShapeOccludes(Shapes.empty(), cullingFace)) {
|
|
- continue;
|
|
- }
|
|
- flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
- }
|
|
+ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
+ }
|
|
|
|
- final int opacity = blockState.getLightBlock(world, this.mutablePos1);
|
|
- final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
|
|
- if (lightLevel > targetLevel) {
|
|
- // it looks like another source propagated here, so re-propagate it
|
|
- if (increaseQueueLength >= increaseQueue.length) {
|
|
- increaseQueue = this.resizeIncreaseQueue();
|
|
- }
|
|
- increaseQueue[increaseQueueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((lightLevel & 0xFL) << (6 + 6 + 16))
|
|
- | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
- | (FLAG_RECHECK_LEVEL | flags);
|
|
- continue;
|
|
+ final int opacity = blockState.getLightBlock();
|
|
+ final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
|
|
+ if (lightLevel > targetLevel) {
|
|
+ // it looks like another source propagated here, so re-propagate it
|
|
+ if (increaseQueueLength >= increaseQueue.length) {
|
|
+ increaseQueue = this.resizeIncreaseQueue();
|
|
}
|
|
- final int emittedLight = blockState.getLightEmission() & emittedMask;
|
|
- if (emittedLight != 0) {
|
|
- // re-propagate source
|
|
- // note: do not set recheck level, or else the propagation will fail
|
|
- if (increaseQueueLength >= increaseQueue.length) {
|
|
- increaseQueue = this.resizeIncreaseQueue();
|
|
- }
|
|
- increaseQueue[increaseQueueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((emittedLight & 0xFL) << (6 + 6 + 16))
|
|
- | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
- | (flags | FLAG_WRITE_LEVEL);
|
|
+ increaseQueue[increaseQueueLength++] =
|
|
+ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
+ | ((lightLevel & 0xFL) << (6 + 6 + 16))
|
|
+ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
+ | (FLAG_RECHECK_LEVEL | flags);
|
|
+ continue;
|
|
+ }
|
|
+ final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.lightEmissionPos)) & emittedMask;
|
|
+ if (emittedLight != 0) {
|
|
+ // re-propagate source
|
|
+ // note: do not set recheck level, or else the propagation will fail
|
|
+ if (increaseQueueLength >= increaseQueue.length) {
|
|
+ increaseQueue = this.resizeIncreaseQueue();
|
|
}
|
|
+ increaseQueue[increaseQueueLength++] =
|
|
+ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
+ | ((emittedLight & 0xFL) << (6 + 6 + 16))
|
|
+ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
+ | (flags | FLAG_WRITE_LEVEL);
|
|
+ }
|
|
|
|
- currentNibble.set(localIndex, 0);
|
|
- this.postLightUpdate(offX, offY, offZ);
|
|
+ currentNibble.set(localIndex, 0);
|
|
+ this.postLightUpdate(offX, offY, offZ);
|
|
|
|
- if (targetLevel > 0) {
|
|
- if (queueLength >= queue.length) {
|
|
- queue = this.resizeDecreaseQueue();
|
|
- }
|
|
- queue[queueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
- | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4))
|
|
- | flags;
|
|
+ if (targetLevel > 0) {
|
|
+ if (queueLength >= queue.length) {
|
|
+ queue = this.resizeDecreaseQueue();
|
|
}
|
|
- continue;
|
|
+ queue[queueLength++] =
|
|
+ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
+ | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
+ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4))
|
|
+ | flags;
|
|
}
|
|
+ continue;
|
|
}
|
|
} else {
|
|
// we actually need to worry about our state here
|
|
final BlockState fromBlock = this.getBlockState(posX, posY, posZ);
|
|
- this.mutablePos2.set(posX, posY, posZ);
|
|
for (final AxisDirection propagate : checkDirections) {
|
|
final int offX = posX + propagate.x;
|
|
final int offY = posY + propagate.y;
|
|
@@ -1446,7 +1356,7 @@ public abstract class StarLightEngine {
|
|
final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset;
|
|
final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8);
|
|
|
|
- final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(world, this.mutablePos2, propagate.nms) : Shapes.empty();
|
|
+ final VoxelShape fromShape = (((StarlightAbstractBlockState)fromBlock).starlight$isConditionallyFullOpaque()) ? fromBlock.getFaceOcclusionShape(propagate.nms) : Shapes.empty();
|
|
|
|
if (fromShape != Shapes.empty() && Shapes.faceShapeOccludes(Shapes.empty(), fromShape)) {
|
|
continue;
|
|
@@ -1464,104 +1374,59 @@ public abstract class StarLightEngine {
|
|
if (blockState == null) {
|
|
continue;
|
|
}
|
|
- final int opacityCached = ((StarlightAbstractBlockState)blockState).starlight$getOpacityIfCached();
|
|
- if (opacityCached != -1) {
|
|
- final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacityCached));
|
|
- if (lightLevel > targetLevel) {
|
|
- // it looks like another source propagated here, so re-propagate it
|
|
- if (increaseQueueLength >= increaseQueue.length) {
|
|
- increaseQueue = this.resizeIncreaseQueue();
|
|
- }
|
|
- increaseQueue[increaseQueueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((lightLevel & 0xFL) << (6 + 6 + 16))
|
|
- | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
- | FLAG_RECHECK_LEVEL;
|
|
- continue;
|
|
- }
|
|
- final int emittedLight = blockState.getLightEmission() & emittedMask;
|
|
- if (emittedLight != 0) {
|
|
- // re-propagate source
|
|
- // note: do not set recheck level, or else the propagation will fail
|
|
- if (increaseQueueLength >= increaseQueue.length) {
|
|
- increaseQueue = this.resizeIncreaseQueue();
|
|
- }
|
|
- increaseQueue[increaseQueueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((emittedLight & 0xFL) << (6 + 6 + 16))
|
|
- | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
- | (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque() ? (FLAG_WRITE_LEVEL | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) : FLAG_WRITE_LEVEL);
|
|
- }
|
|
-
|
|
- currentNibble.set(localIndex, 0);
|
|
- this.postLightUpdate(offX, offY, offZ);
|
|
+ this.lightEmissionPos.set(offX, offY, offZ);
|
|
+ long flags = 0;
|
|
+ if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
|
+ final VoxelShape cullingFace = blockState.getFaceOcclusionShape(propagate.getOpposite().nms);
|
|
|
|
- if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour...
|
|
- if (queueLength >= queue.length) {
|
|
- queue = this.resizeDecreaseQueue();
|
|
- }
|
|
- queue[queueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
- | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4));
|
|
+ if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
|
continue;
|
|
}
|
|
- continue;
|
|
- } else {
|
|
- this.mutablePos1.set(offX, offY, offZ);
|
|
- long flags = 0;
|
|
- if (((StarlightAbstractBlockState)blockState).starlight$isConditionallyFullOpaque()) {
|
|
- final VoxelShape cullingFace = blockState.getFaceOcclusionShape(world, this.mutablePos1, propagate.getOpposite().nms);
|
|
-
|
|
- if (Shapes.faceShapeOccludes(fromShape, cullingFace)) {
|
|
- continue;
|
|
- }
|
|
- flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
- }
|
|
+ flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS;
|
|
+ }
|
|
|
|
- final int opacity = blockState.getLightBlock(world, this.mutablePos1);
|
|
- final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
|
|
- if (lightLevel > targetLevel) {
|
|
- // it looks like another source propagated here, so re-propagate it
|
|
- if (increaseQueueLength >= increaseQueue.length) {
|
|
- increaseQueue = this.resizeIncreaseQueue();
|
|
- }
|
|
- increaseQueue[increaseQueueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((lightLevel & 0xFL) << (6 + 6 + 16))
|
|
- | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
- | (FLAG_RECHECK_LEVEL | flags);
|
|
- continue;
|
|
+ final int opacity = blockState.getLightBlock();
|
|
+ final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity));
|
|
+ if (lightLevel > targetLevel) {
|
|
+ // it looks like another source propagated here, so re-propagate it
|
|
+ if (increaseQueueLength >= increaseQueue.length) {
|
|
+ increaseQueue = this.resizeIncreaseQueue();
|
|
}
|
|
- final int emittedLight = blockState.getLightEmission() & emittedMask;
|
|
- if (emittedLight != 0) {
|
|
- // re-propagate source
|
|
- // note: do not set recheck level, or else the propagation will fail
|
|
- if (increaseQueueLength >= increaseQueue.length) {
|
|
- increaseQueue = this.resizeIncreaseQueue();
|
|
- }
|
|
- increaseQueue[increaseQueueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((emittedLight & 0xFL) << (6 + 6 + 16))
|
|
- | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
- | (flags | FLAG_WRITE_LEVEL);
|
|
+ increaseQueue[increaseQueueLength++] =
|
|
+ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
+ | ((lightLevel & 0xFL) << (6 + 6 + 16))
|
|
+ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
+ | (FLAG_RECHECK_LEVEL | flags);
|
|
+ continue;
|
|
+ }
|
|
+ final int emittedLight = (platformHooks.getLightEmission(blockState, world, this.lightEmissionPos)) & emittedMask;
|
|
+ if (emittedLight != 0) {
|
|
+ // re-propagate source
|
|
+ // note: do not set recheck level, or else the propagation will fail
|
|
+ if (increaseQueueLength >= increaseQueue.length) {
|
|
+ increaseQueue = this.resizeIncreaseQueue();
|
|
}
|
|
+ increaseQueue[increaseQueueLength++] =
|
|
+ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
+ | ((emittedLight & 0xFL) << (6 + 6 + 16))
|
|
+ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4))
|
|
+ | (flags | FLAG_WRITE_LEVEL);
|
|
+ }
|
|
|
|
- currentNibble.set(localIndex, 0);
|
|
- this.postLightUpdate(offX, offY, offZ);
|
|
+ currentNibble.set(localIndex, 0);
|
|
+ this.postLightUpdate(offX, offY, offZ);
|
|
|
|
- if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour...
|
|
- if (queueLength >= queue.length) {
|
|
- queue = this.resizeDecreaseQueue();
|
|
- }
|
|
- queue[queueLength++] =
|
|
- ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
- | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
- | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4))
|
|
- | flags;
|
|
+ if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour...
|
|
+ if (queueLength >= queue.length) {
|
|
+ queue = this.resizeDecreaseQueue();
|
|
}
|
|
- continue;
|
|
+ queue[queueLength++] =
|
|
+ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1))
|
|
+ | ((targetLevel & 0xFL) << (6 + 6 + 16))
|
|
+ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4))
|
|
+ | flags;
|
|
}
|
|
+ continue;
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
|
|
index c64ab41198a5e0c7cbcbe6452af11f82f5938862..571db5f9bf94745a8afe2cd313e593fb15db5e37 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/light/StarLightInterface.java
|
|
@@ -1,8 +1,9 @@
|
|
package ca.spottedleaf.moonrise.patches.starlight.light;
|
|
|
|
import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
|
|
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
|
|
+import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor;
|
|
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
|
+import ca.spottedleaf.concurrentutil.util.Priority;
|
|
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
|
import ca.spottedleaf.moonrise.common.util.WorldUtil;
|
|
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
|
|
@@ -745,34 +746,34 @@ public final class StarLightInterface {
|
|
super(lightInterface);
|
|
}
|
|
|
|
- public void lowerPriority(final int chunkX, final int chunkZ, final PrioritisedExecutor.Priority priority) {
|
|
+ public void lowerPriority(final int chunkX, final int chunkZ, final Priority priority) {
|
|
final ServerChunkTasks task = this.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
if (task != null) {
|
|
task.lowerPriority(priority);
|
|
}
|
|
}
|
|
|
|
- public void setPriority(final int chunkX, final int chunkZ, final PrioritisedExecutor.Priority priority) {
|
|
+ public void setPriority(final int chunkX, final int chunkZ, final Priority priority) {
|
|
final ServerChunkTasks task = this.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
if (task != null) {
|
|
task.setPriority(priority);
|
|
}
|
|
}
|
|
|
|
- public void raisePriority(final int chunkX, final int chunkZ, final PrioritisedExecutor.Priority priority) {
|
|
+ public void raisePriority(final int chunkX, final int chunkZ, final Priority priority) {
|
|
final ServerChunkTasks task = this.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
if (task != null) {
|
|
task.raisePriority(priority);
|
|
}
|
|
}
|
|
|
|
- public PrioritisedExecutor.Priority getPriority(final int chunkX, final int chunkZ) {
|
|
+ public Priority getPriority(final int chunkX, final int chunkZ) {
|
|
final ServerChunkTasks task = this.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
if (task != null) {
|
|
return task.getPriority();
|
|
}
|
|
|
|
- return PrioritisedExecutor.Priority.COMPLETING;
|
|
+ return Priority.COMPLETING;
|
|
}
|
|
|
|
@Override
|
|
@@ -816,7 +817,7 @@ public final class StarLightInterface {
|
|
return ret;
|
|
}
|
|
|
|
- public ServerChunkTasks queueChunkLightTask(final ChunkPos pos, final BooleanSupplier lightTask, final PrioritisedExecutor.Priority priority) {
|
|
+ public ServerChunkTasks queueChunkLightTask(final ChunkPos pos, final BooleanSupplier lightTask, final Priority priority) {
|
|
final ServerChunkTasks ret = this.chunkTasks.compute(CoordinateUtils.getChunkKey(pos), (final long keyInMap, ServerChunkTasks valueInMap) -> {
|
|
if (valueInMap == null) {
|
|
valueInMap = new ServerChunkTasks(
|
|
@@ -879,11 +880,11 @@ public final class StarLightInterface {
|
|
|
|
public ServerChunkTasks(final long chunkCoordinate, final StarLightInterface lightEngine,
|
|
final ServerLightQueue queue) {
|
|
- this(chunkCoordinate, lightEngine, queue, PrioritisedExecutor.Priority.NORMAL);
|
|
+ this(chunkCoordinate, lightEngine, queue, Priority.NORMAL);
|
|
}
|
|
|
|
public ServerChunkTasks(final long chunkCoordinate, final StarLightInterface lightEngine,
|
|
- final ServerLightQueue queue, final PrioritisedExecutor.Priority priority) {
|
|
+ final ServerLightQueue queue, final Priority priority) {
|
|
super(chunkCoordinate, lightEngine, queue);
|
|
this.task = ((ChunkSystemServerLevel)(ServerLevel)lightEngine.getWorld()).moonrise$getChunkTaskScheduler().radiusAwareScheduler.createTask(
|
|
CoordinateUtils.getChunkX(chunkCoordinate), CoordinateUtils.getChunkZ(chunkCoordinate),
|
|
@@ -903,19 +904,19 @@ public final class StarLightInterface {
|
|
return this.task.cancel();
|
|
}
|
|
|
|
- public PrioritisedExecutor.Priority getPriority() {
|
|
+ public Priority getPriority() {
|
|
return this.task.getPriority();
|
|
}
|
|
|
|
- public void lowerPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void lowerPriority(final Priority priority) {
|
|
this.task.lowerPriority(priority);
|
|
}
|
|
|
|
- public void setPriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void setPriority(final Priority priority) {
|
|
this.task.setPriority(priority);
|
|
}
|
|
|
|
- public void raisePriority(final PrioritisedExecutor.Priority priority) {
|
|
+ public void raisePriority(final Priority priority) {
|
|
this.task.raisePriority(priority);
|
|
}
|
|
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/storage/StarlightSectionData.java b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/storage/StarlightSectionData.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..40d004afdc6449530f5bb2d7c7638b8ee3e3a577
|
|
--- /dev/null
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/storage/StarlightSectionData.java
|
|
@@ -0,0 +1,13 @@
|
|
+package ca.spottedleaf.moonrise.patches.starlight.storage;
|
|
+
|
|
+public interface StarlightSectionData {
|
|
+
|
|
+ public int starlight$getBlockLightState();
|
|
+
|
|
+ public void starlight$setBlockLightState(final int state);
|
|
+
|
|
+ public int starlight$getSkyLightState();
|
|
+
|
|
+ public void starlight$setSkyLightState(final int state);
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/util/SaveUtil.java b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/util/SaveUtil.java
|
|
index 57692a503e147a00ac4e1586cd78e12b71a80d3f..689ce367164e79e0426eeecb81dbbc521d4bc742 100644
|
|
--- a/src/main/java/ca/spottedleaf/moonrise/patches/starlight/util/SaveUtil.java
|
|
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/util/SaveUtil.java
|
|
@@ -14,19 +14,20 @@ import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
import org.slf4j.Logger;
|
|
|
|
+// note: keep in-sync with SerializableChunkDataMixin
|
|
public final class SaveUtil {
|
|
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
|
|
- private static final int STARLIGHT_LIGHT_VERSION = 9;
|
|
+ public static final int STARLIGHT_LIGHT_VERSION = 9;
|
|
|
|
public static int getLightVersion() {
|
|
return STARLIGHT_LIGHT_VERSION;
|
|
}
|
|
|
|
- private static final String BLOCKLIGHT_STATE_TAG = "starlight.blocklight_state";
|
|
- private static final String SKYLIGHT_STATE_TAG = "starlight.skylight_state";
|
|
- private static final String STARLIGHT_VERSION_TAG = "starlight.light_version";
|
|
+ public static final String BLOCKLIGHT_STATE_TAG = "starlight.blocklight_state";
|
|
+ public static final String SKYLIGHT_STATE_TAG = "starlight.skylight_state";
|
|
+ public static final String STARLIGHT_VERSION_TAG = "starlight.light_version";
|
|
|
|
public static void saveLightHook(final Level world, final ChunkAccess chunk, final CompoundTag nbt) {
|
|
try {
|
|
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
|
|
index 9bd509915b391e9d382fe47798e2c345b6e59a9a..65c7e7a1b1a5472ee03149bb4ccd9f7d0229069b 100644
|
|
--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
|
|
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
|
|
@@ -242,6 +242,7 @@ public class GlobalConfiguration extends ConfigurationPart {
|
|
|
|
@PostProcess
|
|
private void postProcess() {
|
|
+ ca.spottedleaf.moonrise.common.util.MoonriseCommon.adjustWorkerThreads(this.workerThreads, this.ioThreads);
|
|
ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.init(this);
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/core/MappedRegistry.java b/src/main/java/net/minecraft/core/MappedRegistry.java
|
|
index 71e04e5c1bc0722abf8ca2e0738bd60b6d7ae21c..8e0dfaf02343d74ce786e4fc647bc4c1d73c0014 100644
|
|
--- a/src/main/java/net/minecraft/core/MappedRegistry.java
|
|
+++ b/src/main/java/net/minecraft/core/MappedRegistry.java
|
|
@@ -50,6 +50,19 @@ public class MappedRegistry<T> implements WritableRegistry<T> {
|
|
return this.getTags();
|
|
}
|
|
|
|
+ // Paper start - fluid method optimisations
|
|
+ private void injectFluidRegister(
|
|
+ final ResourceKey<?> resourceKey,
|
|
+ final T object
|
|
+ ) {
|
|
+ if (resourceKey.registryKey() == (Object)net.minecraft.core.registries.Registries.FLUID) {
|
|
+ for (final net.minecraft.world.level.material.FluidState possibleState : ((net.minecraft.world.level.material.Fluid)object).getStateDefinition().getPossibleStates()) {
|
|
+ ((ca.spottedleaf.moonrise.patches.fluid.FluidFluidState)(Object)possibleState).moonrise$initCaches();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Paper end - fluid method optimisations
|
|
+
|
|
public MappedRegistry(ResourceKey<? extends Registry<T>> key, Lifecycle lifecycle) {
|
|
this(key, lifecycle, false);
|
|
}
|
|
@@ -114,6 +127,7 @@ public class MappedRegistry<T> implements WritableRegistry<T> {
|
|
this.toId.put(value, i);
|
|
this.registrationInfos.put(key, info);
|
|
this.registryLifecycle = this.registryLifecycle.add(info.lifecycle());
|
|
+ this.injectFluidRegister(key, value); // Paper - fluid method optimisations
|
|
return reference;
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index 7c388e230c4a88edf6212dd8990e8238d3265ebf..8a66012b7f2396031840c8c718f49f8aab716ee0 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -1106,7 +1106,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
|
}
|
|
// Spigot end
|
|
|
|
- ca.spottedleaf.moonrise.patches.chunk_system.io.RegionFileIOThread.deinit(); // Paper - rewrite chunk system
|
|
+ // Paper start - rewrite chunk system
|
|
+ LOGGER.info("Waiting for I/O tasks to complete...");
|
|
+ ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.flush((MinecraftServer)(Object)this);
|
|
+ LOGGER.info("All I/O tasks to complete");
|
|
+ if ((Object)this instanceof DedicatedServer) {
|
|
+ ca.spottedleaf.moonrise.common.util.MoonriseCommon.haltExecutors();
|
|
+ }
|
|
+ // Paper end - rewrite chunk system
|
|
}
|
|
|
|
public String getLocalIp() {
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
index 8e2c0def0d158bdacfa0d455be82db24cc4bdf64..3b5b6799b6e7832d7cc7d73afb5f2ca04fee6060 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
@@ -252,7 +252,7 @@ public class ChunkHolder extends GenerationChunkHolder implements ca.spottedleaf
|
|
return false;
|
|
} else {
|
|
ichunkaccess.markUnsaved();
|
|
- LevelChunk chunk = this.getChunkToSend(); // Paper - rewrite chunk system
|
|
+ LevelChunk chunk = this.playersSentChunkTo.size() == 0 ? null : this.getChunkToSend(); // Paper - rewrite chunk system
|
|
|
|
if (chunk == null) {
|
|
return false;
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index 60adb0caf3d6f03a57fc55303852070107f1736e..64b9738584fe2efd1ce4a3d7e2c75e091adc2504 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -910,7 +910,6 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
// Paper start - optimise entity tracker
|
|
private void newTrackerTick() {
|
|
- final ca.spottedleaf.moonrise.common.misc.NearbyPlayers nearbyPlayers = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getNearbyPlayers();
|
|
final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup entityLookup = (ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup)((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getEntityLookup();;
|
|
|
|
final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.world.entity.Entity> trackerEntities = entityLookup.trackerEntities;
|
|
@@ -921,21 +920,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
if (tracker == null) {
|
|
continue;
|
|
}
|
|
- ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity)tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition()));
|
|
- tracker.serverEntity.sendChanges();
|
|
- }
|
|
-
|
|
- // process unloads
|
|
- final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.world.entity.Entity> unloadedEntities = entityLookup.trackerUnloadedEntities;
|
|
- final Entity[] unloadedEntitiesRaw = java.util.Arrays.copyOf(unloadedEntities.getRawDataUnchecked(), unloadedEntities.size());
|
|
- unloadedEntities.clear();
|
|
-
|
|
- for (final Entity entity : unloadedEntitiesRaw) {
|
|
- final ChunkMap.TrackedEntity tracker = ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity)entity).moonrise$getTrackedEntity();
|
|
- if (tracker == null) {
|
|
- continue;
|
|
+ ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity)tracker).moonrise$tick(((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)entity).moonrise$getChunkData().nearbyPlayers);
|
|
+ if (((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity)tracker).moonrise$hasPlayers()
|
|
+ || ((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)entity).moonrise$getChunkStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) {
|
|
+ tracker.serverEntity.sendChanges();
|
|
}
|
|
- ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity)tracker).moonrise$clearPlayers();
|
|
}
|
|
}
|
|
// Paper end - optimise entity tracker
|
|
@@ -1172,6 +1161,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
this.removePlayer(player);
|
|
}
|
|
}
|
|
+
|
|
+ @Override
|
|
+ public final boolean moonrise$hasPlayers() {
|
|
+ return !this.seenBy.isEmpty();
|
|
+ }
|
|
// Paper end - optimise entity tracker
|
|
|
|
public TrackedEntity(final Entity entity, final int i, final int j, final boolean flag) {
|
|
@@ -1262,20 +1256,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
|
|
private int getEffectiveRange() {
|
|
- int i = this.range;
|
|
- Iterator iterator = this.entity.getIndirectPassengers().iterator();
|
|
+ // Paper start - optimise entity tracker
|
|
+ final Entity entity = this.entity;
|
|
+ int range = ca.spottedleaf.moonrise.common.PlatformHooks.get().modifyEntityTrackingRange(entity, this.range);
|
|
|
|
- while (iterator.hasNext()) {
|
|
- Entity entity = (Entity) iterator.next();
|
|
- int j = entity.getType().clientTrackingRange() * 16;
|
|
- j = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, j); // Paper
|
|
+ if (entity.getPassengers() == ImmutableList.<Entity>of()) {
|
|
+ return this.scaledRange(range);
|
|
+ }
|
|
|
|
- if (j > i) {
|
|
- i = j;
|
|
- }
|
|
+ // note: we change to List
|
|
+ final List<Entity> passengers = (List<Entity>)entity.getIndirectPassengers();
|
|
+ for (int i = 0, len = passengers.size(); i < len; ++i) {
|
|
+ final Entity passenger = passengers.get(i);
|
|
+ // note: max should be branchless
|
|
+ range = Math.max(range, ca.spottedleaf.moonrise.common.PlatformHooks.get().modifyEntityTrackingRange(passenger, passenger.getType().clientTrackingRange() << 4));
|
|
}
|
|
|
|
- return this.scaledRange(i);
|
|
+ return this.scaledRange(range);
|
|
+ // Paper end - optimise entity tracker
|
|
}
|
|
|
|
public void updatePlayers(List<ServerPlayer> players) {
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index 22157cbc4e4bb1e4010116bdf7429815d46bff2e..23a13bfd23514cde6dcf8d59ba3b43d84f266aad 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -99,7 +99,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler chunkTaskScheduler = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler();
|
|
final CompletableFuture<ChunkAccess> completable = new CompletableFuture<>();
|
|
chunkTaskScheduler.scheduleChunkLoad(
|
|
- chunkX, chunkZ, toStatus, true, ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.BLOCKING,
|
|
+ chunkX, chunkZ, toStatus, true, ca.spottedleaf.concurrentutil.util.Priority.BLOCKING,
|
|
completable::complete
|
|
);
|
|
|
|
@@ -130,6 +130,15 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
return ifPresent;
|
|
}
|
|
|
|
+ final ca.spottedleaf.moonrise.common.PlatformHooks platformHooks = ca.spottedleaf.moonrise.common.PlatformHooks.get();
|
|
+
|
|
+ if (platformHooks.hasCurrentlyLoadingChunk() && currentChunk != null) {
|
|
+ final ChunkAccess loading = platformHooks.getCurrentlyLoadingChunk(currentChunk.vanillaChunkHolder);
|
|
+ if (loading != null && ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) {
|
|
+ return loading;
|
|
+ }
|
|
+ }
|
|
+
|
|
return load ? this.syncLoad(chunkX, chunkZ, toStatus) : null;
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
@@ -254,7 +263,24 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
@Nullable
|
|
@Override
|
|
public LevelChunk getChunkNow(int chunkX, int chunkZ) {
|
|
- return this.fullChunks.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ)); // Paper - rewrite chunk system
|
|
+ // Paper start - rewrite chunk system
|
|
+ final LevelChunk ret = this.fullChunks.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
|
|
+ if (!ca.spottedleaf.moonrise.common.PlatformHooks.get().hasCurrentlyLoadingChunk()) {
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ if (ret != null || !ca.spottedleaf.moonrise.common.util.TickThread.isTickThread()) {
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder holder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler()
|
|
+ .chunkHolderManager.getChunkHolder(chunkX, chunkZ);
|
|
+ if (holder == null) {
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return ca.spottedleaf.moonrise.common.PlatformHooks.get().getCurrentlyLoadingChunk(holder.vanillaChunkHolder);
|
|
+ // Paper end - rewrite chunk system
|
|
}
|
|
|
|
private void clearCache() {
|
|
@@ -311,7 +337,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
|
|
((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().scheduleChunkLoad(
|
|
chunkX, chunkZ, leastStatus, true,
|
|
- ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHER,
|
|
+ ca.spottedleaf.concurrentutil.util.Priority.HIGHER,
|
|
complete
|
|
);
|
|
|
|
@@ -549,7 +575,8 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
|
|
private void getFullChunk(long pos, Consumer<LevelChunk> chunkConsumer) {
|
|
// Paper start - rewrite chunk system
|
|
- final LevelChunk fullChunk = this.getChunkNow(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(pos), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(pos));
|
|
+ // note: bypass currentlyLoaded from getChunkNow
|
|
+ final LevelChunk fullChunk = this.fullChunks.get(pos);
|
|
if (fullChunk != null) {
|
|
chunkConsumer.accept(fullChunk);
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
index af27a004f618912d6a5f1d2c82c8e7e9fb00a037..c7523387f0e9bbfe952abd237a936c8319f10200 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
@@ -775,11 +775,13 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
}
|
|
|
|
// Paper start - optimise random ticking
|
|
+ private final ca.spottedleaf.moonrise.common.util.SimpleRandom simpleRandom = new ca.spottedleaf.moonrise.common.util.SimpleRandom(0L);
|
|
+
|
|
private void optimiseRandomTick(final LevelChunk chunk, final int tickSpeed) {
|
|
final LevelChunkSection[] sections = chunk.getSections();
|
|
final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection((ServerLevel)(Object)this);
|
|
- final RandomSource random = this.random;
|
|
- final boolean tickFluids = false; // Paper - not configurable - MC-224294
|
|
+ final ca.spottedleaf.moonrise.common.util.SimpleRandom simpleRandom = this.simpleRandom;
|
|
+ final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294();
|
|
|
|
final ChunkPos cpos = chunk.getPos();
|
|
final int offsetX = cpos.x << 4;
|
|
@@ -789,39 +791,32 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
final int offsetY = (sectionIndex + minSection) << 4;
|
|
final LevelChunkSection section = sections[sectionIndex];
|
|
final net.minecraft.world.level.chunk.PalettedContainer<net.minecraft.world.level.block.state.BlockState> states = section.states;
|
|
- if (section == null || !section.isRandomlyTickingBlocks()) {
|
|
+ if (!section.isRandomlyTickingBlocks()) {
|
|
continue;
|
|
}
|
|
|
|
- final ca.spottedleaf.moonrise.common.list.IBlockDataList tickList = ((ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection)section).moonrise$getTickingBlockList();
|
|
- if (tickList.size() == 0) {
|
|
- continue;
|
|
- }
|
|
+ final ca.spottedleaf.moonrise.common.list.ShortList tickList = ((ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection)section).moonrise$getTickingBlockList();
|
|
|
|
for (int i = 0; i < tickSpeed; ++i) {
|
|
final int tickingBlocks = tickList.size();
|
|
- final int index = random.nextInt() & ((16 * 16 * 16) - 1);
|
|
+ final int index = simpleRandom.nextInt() & ((16 * 16 * 16) - 1);
|
|
|
|
if (index >= tickingBlocks) {
|
|
// most of the time we fall here
|
|
continue;
|
|
}
|
|
|
|
- final long raw = tickList.getRaw(index);
|
|
- final int location = ca.spottedleaf.moonrise.common.list.IBlockDataList.getLocationFromRaw(raw);
|
|
- final int randomX = (location & 15);
|
|
- final int randomY = ((location >>> (4 + 4)) & 255);
|
|
- final int randomZ = ((location >>> 4) & 15);
|
|
- final BlockState state = states.get(randomX | (randomZ << 4) | (randomY << 8));
|
|
+ final int location = (int)tickList.getRaw(index) & 0xFFFF;
|
|
+ final BlockState state = states.get(location);
|
|
|
|
// do not use a mutable pos, as some random tick implementations store the input without calling immutable()!
|
|
- final BlockPos pos = new BlockPos(randomX | offsetX, randomY | offsetY, randomZ | offsetZ);
|
|
+ final BlockPos pos = new BlockPos((location & 15) | offsetX, ((location >>> (4 + 4)) & 15) | offsetY, ((location >>> 4) & 15) | offsetZ);
|
|
|
|
- state.randomTick((ServerLevel)(Object)this, pos, random);
|
|
- if (tickFluids) {
|
|
+ state.randomTick((ServerLevel)(Object)this, pos, simpleRandom);
|
|
+ if (doubleTickFluids) {
|
|
final FluidState fluidState = state.getFluidState();
|
|
if (fluidState.isRandomlyTicking()) {
|
|
- fluidState.randomTick((ServerLevel)(Object)this, pos, random);
|
|
+ fluidState.randomTick((ServerLevel)(Object)this, pos, simpleRandom);
|
|
}
|
|
}
|
|
}
|
|
@@ -832,6 +827,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
// Paper end - optimise random ticking
|
|
|
|
public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
|
|
+ final ca.spottedleaf.moonrise.common.util.SimpleRandom simpleRandom = this.simpleRandom; // Paper - optimise random ticking
|
|
ChunkPos chunkcoordintpair = chunk.getPos();
|
|
boolean flag = this.isRaining();
|
|
int j = chunkcoordintpair.getMinBlockX();
|
|
@@ -839,7 +835,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
ProfilerFiller gameprofilerfiller = Profiler.get();
|
|
|
|
gameprofilerfiller.push("thunder");
|
|
- if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder
|
|
+ if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && simpleRandom.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder // Paper - optimise random ticking
|
|
BlockPos blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15));
|
|
|
|
if (this.isRainingAt(blockposition)) {
|
|
@@ -871,7 +867,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
|
|
if (!this.paperConfig().environment.disableIceAndSnow) { // Paper - Option to disable ice and snow
|
|
for (int l = 0; l < randomTickSpeed; ++l) {
|
|
- if (this.random.nextInt(48) == 0) {
|
|
+ if (simpleRandom.nextInt(48) == 0) { // Paper - optimise random ticking
|
|
this.tickPrecipitation(this.getBlockRandomPos(j, 0, k, 15));
|
|
}
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
index 4fe3024e26b56c2d796acf703a1bc200ff309f09..7529b3d90e65036c7bf869af30475932d547b3ab 100644
|
|
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
|
@@ -1407,7 +1407,7 @@ public abstract class PlayerList {
|
|
|
|
public void setViewDistance(int viewDistance) {
|
|
this.viewDistance = viewDistance;
|
|
- this.broadcastAll(new ClientboundSetChunkCacheRadiusPacket(viewDistance));
|
|
+ //this.broadcastAll(new ClientboundSetChunkCacheRadiusPacket(viewDistance)); // Paper - rewrite chunk system
|
|
Iterator iterator = this.server.getAllLevels().iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
diff --git a/src/main/java/net/minecraft/util/BitStorage.java b/src/main/java/net/minecraft/util/BitStorage.java
|
|
index 19661e106612b8e4e152085fb398db7bd06acc23..e4e153cb8899e70273aa150b8ea26907cf68b15c 100644
|
|
--- a/src/main/java/net/minecraft/util/BitStorage.java
|
|
+++ b/src/main/java/net/minecraft/util/BitStorage.java
|
|
@@ -24,15 +24,15 @@ public interface BitStorage extends ca.spottedleaf.moonrise.patches.block_counti
|
|
// Paper start - block counting
|
|
// provide default impl in case mods implement this...
|
|
@Override
|
|
- public default it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.ints.IntArrayList> moonrise$countEntries() {
|
|
- final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.ints.IntArrayList> ret = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>();
|
|
+ public default it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.shorts.ShortArrayList> moonrise$countEntries() {
|
|
+ final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.shorts.ShortArrayList> ret = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>();
|
|
|
|
final int size = this.getSize();
|
|
for (int index = 0; index < size; ++index) {
|
|
final int paletteIdx = this.get(index);
|
|
ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
|
- return new it.unimi.dsi.fastutil.ints.IntArrayList();
|
|
- }).add(index);
|
|
+ return new it.unimi.dsi.fastutil.shorts.ShortArrayList();
|
|
+ }).add((short)index);
|
|
}
|
|
|
|
return ret;
|
|
diff --git a/src/main/java/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java b/src/main/java/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java
|
|
index 61dee55417bc802e25b9ba2f271d32d8c12844a9..a8a260a3caaa8e5004069b833ecc8b17b2fc8db5 100644
|
|
--- a/src/main/java/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java
|
|
+++ b/src/main/java/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java
|
|
@@ -7,7 +7,7 @@ import java.util.Iterator;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.core.IdMap;
|
|
|
|
-public class CrudeIncrementalIntIdentityHashBiMap<K> implements IdMap<K> {
|
|
+public class CrudeIncrementalIntIdentityHashBiMap<K> implements IdMap<K>, ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<K> { // Paper - optimise palette reads
|
|
private static final int NOT_FOUND = -1;
|
|
private static final Object EMPTY_SLOT = null;
|
|
private static final float LOADFACTOR = 0.8F;
|
|
@@ -17,6 +17,16 @@ public class CrudeIncrementalIntIdentityHashBiMap<K> implements IdMap<K> {
|
|
private int nextId;
|
|
private int size;
|
|
|
|
+ // Paper start - optimise palette reads
|
|
+ private ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<K> reference;
|
|
+
|
|
+ @Override
|
|
+ public final K[] moonrise$getRawPalette(final ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<K> src) {
|
|
+ this.reference = src;
|
|
+ return this.byId;
|
|
+ }
|
|
+ // Paper end - optimise palette reads
|
|
+
|
|
private CrudeIncrementalIntIdentityHashBiMap(int size) {
|
|
this.keys = (K[])(new Object[size]);
|
|
this.values = new int[size];
|
|
@@ -88,6 +98,12 @@ public class CrudeIncrementalIntIdentityHashBiMap<K> implements IdMap<K> {
|
|
this.byId = crudeIncrementalIntIdentityHashBiMap.byId;
|
|
this.nextId = crudeIncrementalIntIdentityHashBiMap.nextId;
|
|
this.size = crudeIncrementalIntIdentityHashBiMap.size;
|
|
+ // Paper start - optimise palette reads
|
|
+ final ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<K> ref = this.reference;
|
|
+ if (ref != null) {
|
|
+ ref.moonrise$setPalette(this.byId);
|
|
+ }
|
|
+ // Paper end - optimise palette reads
|
|
}
|
|
|
|
public void addMapping(K value, int id) {
|
|
diff --git a/src/main/java/net/minecraft/util/SimpleBitStorage.java b/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
|
index 8acf2f2491a8d9d13392c5e89b2bd5c9918285e1..d99ec470b4653beab630999a5b2c1a6428b20c38 100644
|
|
--- a/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
|
+++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java
|
|
@@ -208,6 +208,20 @@ public class SimpleBitStorage implements BitStorage {
|
|
private final int divideAdd; private final long divideAddUnsigned; // Paper - Perf: Optimize SimpleBitStorage
|
|
private final int divideShift;
|
|
|
|
+ // Paper start - optimise bitstorage read/write operations
|
|
+ private static final int[] BETTER_MAGIC = new int[33];
|
|
+ static {
|
|
+ // 20 bits of precision
|
|
+ // since index is always [0, 4095] (i.e 12 bits), multiplication by a magic value here (20 bits)
|
|
+ // fits exactly in an int and allows us to use integer arithmetic
|
|
+ for (int bits = 1; bits < BETTER_MAGIC.length; ++bits) {
|
|
+ BETTER_MAGIC[bits] = (int)ca.spottedleaf.concurrentutil.util.IntegerUtil.getUnsignedDivisorMagic(64L / bits, 20);
|
|
+ }
|
|
+ }
|
|
+ private final int magic;
|
|
+ private final int mulBits;
|
|
+ // Paper end - optimise bitstorage read/write operations
|
|
+
|
|
public SimpleBitStorage(int elementBits, int size, int[] data) {
|
|
this(elementBits, size);
|
|
int i = 0;
|
|
@@ -261,6 +275,13 @@ public class SimpleBitStorage implements BitStorage {
|
|
} else {
|
|
this.data = new long[j];
|
|
}
|
|
+ // Paper start - optimise bitstorage read/write operations
|
|
+ this.magic = BETTER_MAGIC[this.bits];
|
|
+ this.mulBits = (64 / this.bits) * this.bits;
|
|
+ if (this.size > 4096) {
|
|
+ throw new IllegalStateException("Size > 4096 not supported");
|
|
+ }
|
|
+ // Paper end - optimise bitstorage read/write operations
|
|
}
|
|
|
|
private int cellIndex(int index) {
|
|
@@ -273,31 +294,54 @@ public class SimpleBitStorage implements BitStorage {
|
|
public final int getAndSet(int index, int value) { // Paper - Perf: Optimize SimpleBitStorage
|
|
//Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); // Paper - Perf: Optimize SimpleBitStorage
|
|
//Validate.inclusiveBetween(0L, this.mask, (long)value); // Paper - Perf: Optimize SimpleBitStorage
|
|
- int i = this.cellIndex(index);
|
|
- long l = this.data[i];
|
|
- int j = (index - i * this.valuesPerLong) * this.bits;
|
|
- int k = (int)(l >> j & this.mask);
|
|
- this.data[i] = l & ~(this.mask << j) | ((long)value & this.mask) << j;
|
|
- return k;
|
|
+ // Paper start - optimise bitstorage read/write operations
|
|
+ final int full = this.magic * index; // 20 bits of magic + 12 bits of index = barely int
|
|
+ final int divQ = full >>> 20;
|
|
+ final int divR = (full & 0xFFFFF) * this.mulBits >>> 20;
|
|
+
|
|
+ final long[] dataArray = this.data;
|
|
+
|
|
+ final long data = dataArray[divQ];
|
|
+ final long mask = this.mask;
|
|
+
|
|
+ final long write = data & ~(mask << divR) | ((long)value & mask) << divR;
|
|
+
|
|
+ dataArray[divQ] = write;
|
|
+
|
|
+ return (int)(data >>> divR & mask);
|
|
+ // Paper end - optimise bitstorage read/write operations
|
|
}
|
|
|
|
@Override
|
|
public final void set(int index, int value) { // Paper - Perf: Optimize SimpleBitStorage
|
|
//Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); // Paper - Perf: Optimize SimpleBitStorage
|
|
//Validate.inclusiveBetween(0L, this.mask, (long)value); // Paper - Perf: Optimize SimpleBitStorage
|
|
- int i = this.cellIndex(index);
|
|
- long l = this.data[i];
|
|
- int j = (index - i * this.valuesPerLong) * this.bits;
|
|
- this.data[i] = l & ~(this.mask << j) | ((long)value & this.mask) << j;
|
|
+ // Paper start - optimise bitstorage read/write operations
|
|
+ final int full = this.magic * index; // 20 bits of magic + 12 bits of index = barely int
|
|
+ final int divQ = full >>> 20;
|
|
+ final int divR = (full & 0xFFFFF) * this.mulBits >>> 20;
|
|
+
|
|
+ final long[] dataArray = this.data;
|
|
+
|
|
+ final long data = dataArray[divQ];
|
|
+ final long mask = this.mask;
|
|
+
|
|
+ final long write = data & ~(mask << divR) | ((long)value & mask) << divR;
|
|
+
|
|
+ dataArray[divQ] = write;
|
|
+ // Paper end - optimise bitstorage read/write operations
|
|
}
|
|
|
|
@Override
|
|
public final int get(int index) { // Paper - Perf: Optimize SimpleBitStorage
|
|
//Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)index); // Paper - Perf: Optimize SimpleBitStorage
|
|
- int i = this.cellIndex(index);
|
|
- long l = this.data[i];
|
|
- int j = (index - i * this.valuesPerLong) * this.bits;
|
|
- return (int)(l >> j & this.mask);
|
|
+ // Paper start - optimise bitstorage read/write operations
|
|
+ final int full = this.magic * index; // 20 bits of magic + 12 bits of index = barely int
|
|
+ final int divQ = full >>> 20;
|
|
+ final int divR = (full & 0xFFFFF) * this.mulBits >>> 20;
|
|
+
|
|
+ return (int)(this.data[divQ] >>> divR & this.mask);
|
|
+ // Paper end - optimise bitstorage read/write operations
|
|
}
|
|
|
|
@Override
|
|
@@ -364,35 +408,62 @@ public class SimpleBitStorage implements BitStorage {
|
|
|
|
// Paper start - block counting
|
|
@Override
|
|
- public final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.ints.IntArrayList> moonrise$countEntries() {
|
|
+ public final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.shorts.ShortArrayList> moonrise$countEntries() {
|
|
final int valuesPerLong = this.valuesPerLong;
|
|
final int bits = this.bits;
|
|
- final long mask = this.mask;
|
|
+ final long mask = (1L << bits) - 1L;
|
|
final int size = this.size;
|
|
|
|
- // we may be backed by global palette, so limit bits for init capacity
|
|
- final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.ints.IntArrayList> ret = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(
|
|
- 1 << Math.min(6, bits)
|
|
- );
|
|
-
|
|
- int index = 0;
|
|
-
|
|
- for (long value : this.data) {
|
|
- int li = 0;
|
|
- do {
|
|
- final int paletteIdx = (int)(value & mask);
|
|
- value >>= bits;
|
|
+ if (bits <= 6) {
|
|
+ final it.unimi.dsi.fastutil.shorts.ShortArrayList[] byId = new it.unimi.dsi.fastutil.shorts.ShortArrayList[1 << bits];
|
|
+ final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.shorts.ShortArrayList> ret = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(1 << bits);
|
|
+
|
|
+ int index = 0;
|
|
+
|
|
+ for (long value : this.data) {
|
|
+ int li = 0;
|
|
+ do {
|
|
+ final int paletteIdx = (int)(value & mask);
|
|
+ value >>= bits;
|
|
+ ++li;
|
|
+
|
|
+ final it.unimi.dsi.fastutil.shorts.ShortArrayList coords = byId[paletteIdx];
|
|
+ if (coords != null) {
|
|
+ coords.add((short)index++);
|
|
+ continue;
|
|
+ } else {
|
|
+ final it.unimi.dsi.fastutil.shorts.ShortArrayList newCoords = new it.unimi.dsi.fastutil.shorts.ShortArrayList(64);
|
|
+ byId[paletteIdx] = newCoords;
|
|
+ newCoords.add((short)index++);
|
|
+ ret.put(paletteIdx, newCoords);
|
|
+ continue;
|
|
+ }
|
|
+ } while (li < valuesPerLong && index < size);
|
|
+ }
|
|
|
|
- ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
|
- return new it.unimi.dsi.fastutil.ints.IntArrayList();
|
|
- }).add(index);
|
|
+ return ret;
|
|
+ } else {
|
|
+ final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.shorts.ShortArrayList> ret = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(
|
|
+ 1 << 6
|
|
+ );
|
|
+
|
|
+ int index = 0;
|
|
+
|
|
+ for (long value : this.data) {
|
|
+ int li = 0;
|
|
+ do {
|
|
+ final int paletteIdx = (int)(value & mask);
|
|
+ value >>= bits;
|
|
+ ++li;
|
|
+
|
|
+ ret.computeIfAbsent(paletteIdx, (final int key) -> {
|
|
+ return new it.unimi.dsi.fastutil.shorts.ShortArrayList(64);
|
|
+ }).add((short)index++);
|
|
+ } while (li < valuesPerLong && index < size);
|
|
+ }
|
|
|
|
- ++li;
|
|
- ++index;
|
|
- } while (li < valuesPerLong && index < size);
|
|
+ return ret;
|
|
}
|
|
-
|
|
- return ret;
|
|
}
|
|
// Paper end - block counting
|
|
|
|
diff --git a/src/main/java/net/minecraft/util/ZeroBitStorage.java b/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
|
index 15c5164d0ef41a978c16ee317fa73e97f2480207..1f9c436a632e4f110be61cf76fcfc3b7eb80334e 100644
|
|
--- a/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
|
+++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java
|
|
@@ -65,17 +65,17 @@ public class ZeroBitStorage implements BitStorage {
|
|
|
|
// Paper start - block counting
|
|
@Override
|
|
- public final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.ints.IntArrayList> moonrise$countEntries() {
|
|
+ public final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.shorts.ShortArrayList> moonrise$countEntries() {
|
|
final int size = this.size;
|
|
|
|
- final int[] raw = new int[size];
|
|
+ final short[] raw = new short[size];
|
|
for (int i = 0; i < size; ++i) {
|
|
- raw[i] = i;
|
|
+ raw[i] = (short)i;
|
|
}
|
|
|
|
- final it.unimi.dsi.fastutil.ints.IntArrayList coordinates = it.unimi.dsi.fastutil.ints.IntArrayList.wrap(raw, size);
|
|
+ final it.unimi.dsi.fastutil.shorts.ShortArrayList coordinates = it.unimi.dsi.fastutil.shorts.ShortArrayList.wrap(raw, size);
|
|
|
|
- final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.ints.IntArrayList> ret = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(1);
|
|
+ final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.shorts.ShortArrayList> ret = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(1);
|
|
ret.put(0, coordinates);
|
|
return ret;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
index 5410a0380c44629f1c9b4f0a8e6017cfc5a31a89..a3b0363fbc207ed9edc8a4d6619b6fff9389a9c7 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
|
@@ -448,6 +448,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
// Paper start - rewrite chunk system
|
|
private final boolean isHardColliding = this.moonrise$isHardCollidingUncached();
|
|
private net.minecraft.server.level.FullChunkStatus chunkStatus;
|
|
+ private ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData chunkData;
|
|
private int sectionX = Integer.MIN_VALUE;
|
|
private int sectionY = Integer.MIN_VALUE;
|
|
private int sectionZ = Integer.MIN_VALUE;
|
|
@@ -468,6 +469,16 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
this.chunkStatus = status;
|
|
}
|
|
|
|
+ @Override
|
|
+ public final ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData moonrise$getChunkData() {
|
|
+ return this.chunkData;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final void moonrise$setChunkData(final ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData chunkData) {
|
|
+ this.chunkData = chunkData;
|
|
+ }
|
|
+
|
|
@Override
|
|
public final int moonrise$getSectionX() {
|
|
return this.sectionX;
|
|
@@ -516,6 +527,54 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return this.getIndirectPassengersStream().anyMatch((entity) -> entity instanceof Player);
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
+ // Paper start - optimise collisions
|
|
+ private static float[] calculateStepHeights(final AABB box, final List<VoxelShape> voxels, final List<AABB> aabbs, final float stepHeight,
|
|
+ final float collidedY) {
|
|
+ final FloatArraySet ret = new FloatArraySet();
|
|
+
|
|
+ for (int i = 0, len = voxels.size(); i < len; ++i) {
|
|
+ final VoxelShape shape = voxels.get(i);
|
|
+
|
|
+ final double[] yCoords = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape).moonrise$rootCoordinatesY();
|
|
+ final double yOffset = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape).moonrise$offsetY();
|
|
+
|
|
+ for (final double yUnoffset : yCoords) {
|
|
+ final double y = yUnoffset + yOffset;
|
|
+
|
|
+ final float step = (float)(y - box.minY);
|
|
+
|
|
+ if (step > stepHeight) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (step < 0.0f || !(step != collidedY)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ ret.add(step);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (int i = 0, len = aabbs.size(); i < len; ++i) {
|
|
+ final AABB shape = aabbs.get(i);
|
|
+
|
|
+ final float step1 = (float)(shape.minY - box.minY);
|
|
+ final float step2 = (float)(shape.maxY - box.minY);
|
|
+
|
|
+ if (!(step1 < 0.0f) && step1 != collidedY && !(step1 > stepHeight)) {
|
|
+ ret.add(step1);
|
|
+ }
|
|
+
|
|
+ if (!(step2 < 0.0f) && step2 != collidedY && !(step2 > stepHeight)) {
|
|
+ ret.add(step2);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final float[] steps = ret.toFloatArray();
|
|
+ FloatArrays.unstableSort(steps);
|
|
+ return steps;
|
|
+ }
|
|
+ // Paper end - optimise collisions
|
|
// Paper start - optimise entity tracker
|
|
private net.minecraft.server.level.ChunkMap.TrackedEntity trackedEntity;
|
|
|
|
@@ -1402,73 +1461,67 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return movement;
|
|
}
|
|
|
|
- final Level world = this.level;
|
|
- final AABB currBoundingBox = this.getBoundingBox();
|
|
-
|
|
- if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isEmpty(currBoundingBox)) {
|
|
- return movement;
|
|
- }
|
|
+ final AABB currentBox = this.getBoundingBox();
|
|
|
|
- final List<AABB> potentialCollisionsBB = new ArrayList<>();
|
|
final List<VoxelShape> potentialCollisionsVoxel = new ArrayList<>();
|
|
- final double stepHeight = (double)this.maxUpStep();
|
|
- final AABB collisionBox;
|
|
- final boolean onGround = this.onGround;
|
|
+ final List<AABB> potentialCollisionsBB = new ArrayList<>();
|
|
|
|
+ final AABB initialCollisionBox;
|
|
if (xZero & zZero) {
|
|
- if (movement.y > 0.0) {
|
|
- collisionBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.cutUpwards(currBoundingBox, movement.y);
|
|
- } else {
|
|
- collisionBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.cutDownwards(currBoundingBox, movement.y);
|
|
- }
|
|
+ // note: xZero & zZero -> collision on x/z == 0 -> no step height calculation
|
|
+ // this specifically optimises entities standing still
|
|
+ initialCollisionBox = movement.y < 0.0 ?
|
|
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.cutDownwards(currentBox, movement.y) : ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.cutUpwards(currentBox, movement.y);
|
|
} else {
|
|
- // note: xZero == false or zZero == false
|
|
- if (stepHeight > 0.0 && (onGround || (movement.y < 0.0))) {
|
|
- // don't bother getting the collisions if we don't need them.
|
|
- if (movement.y <= 0.0) {
|
|
- collisionBox = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.expandUpwards(currBoundingBox.expandTowards(movement.x, movement.y, movement.z), stepHeight);
|
|
- } else {
|
|
- collisionBox = currBoundingBox.expandTowards(movement.x, Math.max(stepHeight, movement.y), movement.z);
|
|
- }
|
|
- } else {
|
|
- collisionBox = currBoundingBox.expandTowards(movement.x, movement.y, movement.z);
|
|
- }
|
|
+ initialCollisionBox = currentBox.expandTowards(movement);
|
|
}
|
|
|
|
- ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisions(
|
|
- world, (Entity)(Object)this, collisionBox, potentialCollisionsVoxel, potentialCollisionsBB,
|
|
- ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER,
|
|
- null, null
|
|
+ final List<AABB> entityAABBs = new ArrayList<>();
|
|
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getEntityHardCollisions(
|
|
+ this.level, (Entity)(Object)this, initialCollisionBox, entityAABBs, 0, null
|
|
);
|
|
|
|
- if (potentialCollisionsVoxel.isEmpty() && potentialCollisionsBB.isEmpty()) {
|
|
- return movement;
|
|
- }
|
|
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisionsForBlocksOrWorldBorder(
|
|
+ this.level, (Entity)(Object)this, initialCollisionBox, potentialCollisionsVoxel, potentialCollisionsBB,
|
|
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER, null
|
|
+ );
|
|
+ potentialCollisionsBB.addAll(entityAABBs);
|
|
+ final Vec3 collided = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currentBox, potentialCollisionsVoxel, potentialCollisionsBB);
|
|
|
|
- final Vec3 limitedMoveVector = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB);
|
|
+ final boolean collidedX = collided.x != movement.x;
|
|
+ final boolean collidedY = collided.y != movement.y;
|
|
+ final boolean collidedZ = collided.z != movement.z;
|
|
|
|
- if (stepHeight > 0.0
|
|
- && (onGround || (limitedMoveVector.y != movement.y && movement.y < 0.0))
|
|
- && (limitedMoveVector.x != movement.x || limitedMoveVector.z != movement.z)) {
|
|
- Vec3 vec3d2 = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(new Vec3(movement.x, stepHeight, movement.z), currBoundingBox, potentialCollisionsVoxel, potentialCollisionsBB);
|
|
- final Vec3 vec3d3 = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(new Vec3(0.0, stepHeight, 0.0), currBoundingBox.expandTowards(movement.x, 0.0, movement.z), potentialCollisionsVoxel, potentialCollisionsBB);
|
|
+ final boolean collidedDownwards = collidedY && movement.y < 0.0;
|
|
|
|
- if (vec3d3.y < stepHeight) {
|
|
- final Vec3 vec3d4 = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(new Vec3(movement.x, 0.0D, movement.z), currBoundingBox.move(vec3d3), potentialCollisionsVoxel, potentialCollisionsBB).add(vec3d3);
|
|
+ final double stepHeight;
|
|
|
|
- if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) {
|
|
- vec3d2 = vec3d4;
|
|
- }
|
|
- }
|
|
+ if ((!collidedDownwards && !this.onGround) || (!collidedX && !collidedZ) || (stepHeight = (double)this.maxUpStep()) <= 0.0) {
|
|
+ return collided;
|
|
+ }
|
|
|
|
- if (vec3d2.horizontalDistanceSqr() > limitedMoveVector.horizontalDistanceSqr()) {
|
|
- return vec3d2.add(ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(new Vec3(0.0D, -vec3d2.y + movement.y, 0.0D), currBoundingBox.move(vec3d2), potentialCollisionsVoxel, potentialCollisionsBB));
|
|
- }
|
|
+ final AABB collidedYBox = collidedDownwards ? currentBox.move(0.0, collided.y, 0.0) : currentBox;
|
|
+ AABB stepRetrievalBox = collidedYBox.expandTowards(movement.x, stepHeight, movement.z);
|
|
+ if (!collidedDownwards) {
|
|
+ stepRetrievalBox = stepRetrievalBox.expandTowards(0.0, (double)-1.0E-5F, 0.0);
|
|
+ }
|
|
|
|
- return limitedMoveVector;
|
|
- } else {
|
|
- return limitedMoveVector;
|
|
+ final List<VoxelShape> stepVoxels = new ArrayList<>();
|
|
+ final List<AABB> stepAABBs = entityAABBs;
|
|
+
|
|
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisionsForBlocksOrWorldBorder(
|
|
+ this.level, (Entity)(Object)this, stepRetrievalBox, stepVoxels, stepAABBs,
|
|
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER, null
|
|
+ );
|
|
+
|
|
+ for (final float step : calculateStepHeights(collidedYBox, stepVoxels, stepAABBs, (float)stepHeight, (float)collided.y)) {
|
|
+ final Vec3 stepResult = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(new Vec3(movement.x, (double)step, movement.z), collidedYBox, stepVoxels, stepAABBs);
|
|
+ if (stepResult.horizontalDistanceSqr() > collided.horizontalDistanceSqr()) {
|
|
+ return stepResult.add(0.0, collidedYBox.minY - currentBox.minY, 0.0);
|
|
+ }
|
|
}
|
|
+
|
|
+ return collided;
|
|
// Paper end - optimise collisions
|
|
}
|
|
|
|
@@ -2836,64 +2889,99 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return false;
|
|
}
|
|
|
|
- final float reducedWith = this.dimensions.width() * 0.8F;
|
|
- final AABB box = AABB.ofSize(this.getEyePosition(), reducedWith, 1.0E-6D, reducedWith);
|
|
+ final double reducedWith = (double)(this.dimensions.width() * 0.8F);
|
|
+ final AABB boundingBox = AABB.ofSize(this.getEyePosition(), reducedWith, 1.0E-6D, reducedWith);
|
|
+ final Level world = this.level;
|
|
|
|
- if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isEmpty(box)) {
|
|
+ if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isEmpty(boundingBox)) {
|
|
return false;
|
|
}
|
|
|
|
- final BlockPos.MutableBlockPos tempPos = new BlockPos.MutableBlockPos();
|
|
+ final int minBlockX = Mth.floor(boundingBox.minX);
|
|
+ final int minBlockY = Mth.floor(boundingBox.minY);
|
|
+ final int minBlockZ = Mth.floor(boundingBox.minZ);
|
|
+
|
|
+ final int maxBlockX = Mth.floor(boundingBox.maxX);
|
|
+ final int maxBlockY = Mth.floor(boundingBox.maxY);
|
|
+ final int maxBlockZ = Mth.floor(boundingBox.maxZ);
|
|
|
|
- final int minX = Mth.floor(box.minX);
|
|
- final int minY = Mth.floor(box.minY);
|
|
- final int minZ = Mth.floor(box.minZ);
|
|
- final int maxX = Mth.floor(box.maxX);
|
|
- final int maxY = Mth.floor(box.maxY);
|
|
- final int maxZ = Mth.floor(box.maxZ);
|
|
+ final int minChunkX = minBlockX >> 4;
|
|
+ final int minChunkY = minBlockY >> 4;
|
|
+ final int minChunkZ = minBlockZ >> 4;
|
|
|
|
- final net.minecraft.world.level.chunk.ChunkSource chunkProvider = this.level.getChunkSource();
|
|
+ final int maxChunkX = maxBlockX >> 4;
|
|
+ final int maxChunkY = maxBlockY >> 4;
|
|
+ final int maxChunkZ = maxBlockZ >> 4;
|
|
|
|
- long lastChunkKey = ChunkPos.INVALID_CHUNK_POS;
|
|
- net.minecraft.world.level.chunk.LevelChunk lastChunk = null;
|
|
- for (int fz = minZ; fz <= maxZ; ++fz) {
|
|
- tempPos.setZ(fz);
|
|
- for (int fx = minX; fx <= maxX; ++fx) {
|
|
- final int newChunkX = fx >> 4;
|
|
- final int newChunkZ = fz >> 4;
|
|
- final net.minecraft.world.level.chunk.LevelChunk chunk = lastChunkKey == (lastChunkKey = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(newChunkX, newChunkZ)) ?
|
|
- lastChunk : (lastChunk = (net.minecraft.world.level.chunk.LevelChunk)chunkProvider.getChunk(newChunkX, newChunkZ, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, true));
|
|
- tempPos.setX(fx);
|
|
- for (int fy = minY; fy <= maxY; ++fy) {
|
|
- tempPos.setY(fy);
|
|
+ final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(world);
|
|
+ final net.minecraft.world.level.chunk.ChunkSource chunkSource = world.getChunkSource();
|
|
+ final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
|
|
|
- final BlockState state = chunk.getBlockState(tempPos);
|
|
+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
|
+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
|
+ final net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, true).getSections();
|
|
|
|
- if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)state).moonrise$emptyCollisionShape() || !state.isSuffocating(this.level, tempPos)) {
|
|
+ for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
|
+ final int sectionIdx = currChunkY - minSection;
|
|
+ if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
|
continue;
|
|
}
|
|
-
|
|
- // Yes, it does not use the Entity context stuff.
|
|
- final VoxelShape collisionShape = state.getCollisionShape(this.level, tempPos);
|
|
-
|
|
- if (collisionShape.isEmpty()) {
|
|
+ final net.minecraft.world.level.chunk.LevelChunkSection section = sections[sectionIdx];
|
|
+ if (section.hasOnlyAir()) {
|
|
+ // empty
|
|
continue;
|
|
}
|
|
|
|
- final AABB toCollide = box.move(-(double)fx, -(double)fy, -(double)fz);
|
|
+ final net.minecraft.world.level.chunk.PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.states;
|
|
+
|
|
+ final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
|
+ final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
|
+ final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
|
+ final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
|
+ final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
|
+ final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
|
+
|
|
+ for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
|
+ final int blockY = currY | (currChunkY << 4);
|
|
+ mutablePos.setY(blockY);
|
|
+ for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
|
+ final int blockZ = currZ | (currChunkZ << 4);
|
|
+ mutablePos.setZ(blockZ);
|
|
+ for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
|
+ final int blockX = currX | (currChunkX << 4);
|
|
+ mutablePos.setX(blockX);
|
|
+
|
|
+ final BlockState blockState = blocks.get((currX) | (currZ << 4) | ((currY) << 8));
|
|
+
|
|
+ if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)blockState).moonrise$emptyCollisionShape()
|
|
+ || !blockState.isSuffocating(world, mutablePos)) {
|
|
+ continue;
|
|
+ }
|
|
|
|
- final AABB singleAABB = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)collisionShape).moonrise$getSingleAABBRepresentation();
|
|
- if (singleAABB != null) {
|
|
- if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersect(singleAABB, toCollide)) {
|
|
- return true;
|
|
- }
|
|
- continue;
|
|
- }
|
|
+ // Yes, it does not use the Entity context stuff.
|
|
+ final VoxelShape collisionShape = blockState.getCollisionShape(world, mutablePos);
|
|
+
|
|
+ if (collisionShape.isEmpty()) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ final AABB toCollide = boundingBox.move(-(double)blockX, -(double)blockY, -(double)blockZ);
|
|
|
|
- if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersectNoEmpty(collisionShape, toCollide)) {
|
|
- return true;
|
|
+ final AABB singleAABB = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)collisionShape).moonrise$getSingleAABBRepresentation();
|
|
+ if (singleAABB != null) {
|
|
+ if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersect(singleAABB, toCollide)) {
|
|
+ return true;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersectNoEmpty(collisionShape, toCollide)) {
|
|
+ return true;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
}
|
|
- continue;
|
|
}
|
|
}
|
|
}
|
|
@@ -4508,82 +4596,136 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
|
return Mth.lerp(delta, this.yRotO, this.yRot);
|
|
}
|
|
|
|
- public boolean updateFluidHeightAndDoFluidPushing(TagKey<Fluid> tag, double speed) {
|
|
+ // Paper start - optimise collisions
|
|
+ public boolean updateFluidHeightAndDoFluidPushing(final TagKey<Fluid> fluid, final double flowScale) {
|
|
if (this.touchingUnloadedChunk()) {
|
|
return false;
|
|
- } else {
|
|
- AABB axisalignedbb = this.getBoundingBox().deflate(0.001D);
|
|
- int i = Mth.floor(axisalignedbb.minX);
|
|
- int j = Mth.ceil(axisalignedbb.maxX);
|
|
- int k = Mth.floor(axisalignedbb.minY);
|
|
- int l = Mth.ceil(axisalignedbb.maxY);
|
|
- int i1 = Mth.floor(axisalignedbb.minZ);
|
|
- int j1 = Mth.ceil(axisalignedbb.maxZ);
|
|
- double d1 = 0.0D;
|
|
- boolean flag = this.isPushedByFluid();
|
|
- boolean flag1 = false;
|
|
- Vec3 vec3d = Vec3.ZERO;
|
|
- int k1 = 0;
|
|
- BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
|
|
-
|
|
- for (int l1 = i; l1 < j; ++l1) {
|
|
- for (int i2 = k; i2 < l; ++i2) {
|
|
- for (int j2 = i1; j2 < j1; ++j2) {
|
|
- blockposition_mutableblockposition.set(l1, i2, j2);
|
|
- FluidState fluid = this.level().getFluidState(blockposition_mutableblockposition);
|
|
-
|
|
- if (fluid.is(tag)) {
|
|
- double d2 = (double) ((float) i2 + fluid.getHeight(this.level(), blockposition_mutableblockposition));
|
|
-
|
|
- if (d2 >= axisalignedbb.minY) {
|
|
- flag1 = true;
|
|
- d1 = Math.max(d2 - axisalignedbb.minY, d1);
|
|
- if (flag) {
|
|
- Vec3 vec3d1 = fluid.getFlow(this.level(), blockposition_mutableblockposition);
|
|
-
|
|
- if (d1 < 0.4D) {
|
|
- vec3d1 = vec3d1.scale(d1);
|
|
- }
|
|
+ }
|
|
+
|
|
+ final AABB boundingBox = this.getBoundingBox().deflate(1.0E-3);
|
|
+
|
|
+ final Level world = this.level;
|
|
+ final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(world);
|
|
+
|
|
+ final int minBlockX = Mth.floor(boundingBox.minX);
|
|
+ final int minBlockY = Math.max((minSection << 4), Mth.floor(boundingBox.minY));
|
|
+ final int minBlockZ = Mth.floor(boundingBox.minZ);
|
|
+
|
|
+ // note: bounds are exclusive in Vanilla, so we subtract 1 - our loop expects bounds to be inclusive
|
|
+ final int maxBlockX = Mth.ceil(boundingBox.maxX) - 1;
|
|
+ final int maxBlockY = Math.min((ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(world) << 4) | 15, Mth.ceil(boundingBox.maxY) - 1);
|
|
+ final int maxBlockZ = Mth.ceil(boundingBox.maxZ) - 1;
|
|
+
|
|
+ final boolean isPushable = this.isPushedByFluid();
|
|
+ final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
|
+
|
|
+ Vec3 pushVector = Vec3.ZERO;
|
|
+ double totalPushes = 0.0;
|
|
+ double maxHeightDiff = 0.0;
|
|
+ boolean inFluid = false;
|
|
+
|
|
+ final int minChunkX = minBlockX >> 4;
|
|
+ final int maxChunkX = maxBlockX >> 4;
|
|
+
|
|
+ final int minChunkY = minBlockY >> 4;
|
|
+ final int maxChunkY = maxBlockY >> 4;
|
|
+
|
|
+ final int minChunkZ = minBlockZ >> 4;
|
|
+ final int maxChunkZ = maxBlockZ >> 4;
|
|
+
|
|
+ final net.minecraft.world.level.chunk.ChunkSource chunkSource = world.getChunkSource();
|
|
+
|
|
+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
|
+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
|
+ final net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunkSource.getChunk(currChunkX, currChunkZ, net.minecraft.world.level.chunk.status.ChunkStatus.FULL, false).getSections();
|
|
|
|
- vec3d = vec3d.add(vec3d1);
|
|
- ++k1;
|
|
+ // bound y
|
|
+ for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
|
+ final int sectionIdx = currChunkY - minSection;
|
|
+ if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
|
+ continue;
|
|
+ }
|
|
+ final net.minecraft.world.level.chunk.LevelChunkSection section = sections[sectionIdx];
|
|
+ if (section.hasOnlyAir()) {
|
|
+ // empty
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ final net.minecraft.world.level.chunk.PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.states;
|
|
+
|
|
+ final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) : 0;
|
|
+ final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) : 15;
|
|
+ final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) : 0;
|
|
+ final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) : 15;
|
|
+ final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) : 0;
|
|
+ final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) : 15;
|
|
+
|
|
+ for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
|
+ for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
|
+ for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
|
+ final FluidState fluidState = blocks.get((currX) | (currZ << 4) | ((currY) << 8)).getFluidState();
|
|
+
|
|
+ if (fluidState.isEmpty() || !fluidState.is(fluid)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ mutablePos.set(currX | (currChunkX << 4), currY | (currChunkY << 4), currZ | (currChunkZ << 4));
|
|
+
|
|
+ final double height = (double)((float)mutablePos.getY() + fluidState.getHeight(world, mutablePos));
|
|
+ final double diff = height - boundingBox.minY;
|
|
+
|
|
+ if (diff < 0.0) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ inFluid = true;
|
|
+ maxHeightDiff = Math.max(maxHeightDiff, diff);
|
|
+
|
|
+ if (!isPushable) {
|
|
+ continue;
|
|
}
|
|
- // CraftBukkit start - store last lava contact location
|
|
- if (tag == FluidTags.LAVA) {
|
|
- this.lastLavaContact = blockposition_mutableblockposition.immutable();
|
|
+
|
|
+ ++totalPushes;
|
|
+
|
|
+ final Vec3 flow = fluidState.getFlow(world, mutablePos);
|
|
+
|
|
+ if (diff < 0.4) {
|
|
+ pushVector = pushVector.add(flow.scale(diff));
|
|
+ } else {
|
|
+ pushVector = pushVector.add(flow);
|
|
}
|
|
- // CraftBukkit end
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
+ }
|
|
|
|
- if (vec3d.length() > 0.0D) {
|
|
- if (k1 > 0) {
|
|
- vec3d = vec3d.scale(1.0D / (double) k1);
|
|
- }
|
|
+ this.fluidHeight.put(fluid, maxHeightDiff);
|
|
|
|
- if (!(this instanceof Player)) {
|
|
- vec3d = vec3d.normalize();
|
|
- }
|
|
+ if (pushVector.lengthSqr() == 0.0) {
|
|
+ return inFluid;
|
|
+ }
|
|
|
|
- Vec3 vec3d2 = this.getDeltaMovement();
|
|
+ // note: totalPushes != 0 as pushVector != 0
|
|
+ pushVector = pushVector.scale(1.0 / totalPushes);
|
|
+ final Vec3 currMovement = this.getDeltaMovement();
|
|
|
|
- vec3d = vec3d.scale(speed);
|
|
- double d3 = 0.003D;
|
|
+ if (!((Entity)(Object)this instanceof Player)) {
|
|
+ pushVector = pushVector.normalize();
|
|
+ }
|
|
|
|
- if (Math.abs(vec3d2.x) < 0.003D && Math.abs(vec3d2.z) < 0.003D && vec3d.length() < 0.0045000000000000005D) {
|
|
- vec3d = vec3d.normalize().scale(0.0045000000000000005D);
|
|
- }
|
|
+ pushVector = pushVector.scale(flowScale);
|
|
+ if (Math.abs(currMovement.x) < 0.003 && Math.abs(currMovement.z) < 0.003 && pushVector.length() < 0.0045000000000000005) {
|
|
+ pushVector = pushVector.normalize().scale(0.0045000000000000005);
|
|
+ }
|
|
|
|
- this.setDeltaMovement(this.getDeltaMovement().add(vec3d));
|
|
- }
|
|
+ this.setDeltaMovement(currMovement.add(pushVector));
|
|
|
|
- this.fluidHeight.put(tag, d1);
|
|
- return flag1;
|
|
- }
|
|
+ // note: inFluid = true here as pushVector != 0
|
|
+ return true;
|
|
}
|
|
+ // Paper end - optimise collisions
|
|
|
|
public boolean touchingUnloadedChunk() {
|
|
AABB axisalignedbb = this.getBoundingBox().inflate(1.0D);
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
|
|
index 4cf6cb0abfeb7065c6d9381fb4194371c0cddc35..5930a430983061afddf20e3208ff2462ca1b78cd 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
|
|
@@ -73,8 +73,7 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
|
|
|
|
ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
|
|
|
|
- final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager manager = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager;
|
|
- final ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
|
|
+ final ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk ret = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
|
|
|
|
return ret == null ? Optional.empty() : ret.getSectionForVanilla(chunkY);
|
|
}
|
|
@@ -128,9 +127,13 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
|
|
public final void moonrise$onUnload(final long coordinate) { // Paper - rewrite chunk system
|
|
final int chunkX = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(coordinate);
|
|
final int chunkZ = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(coordinate);
|
|
+
|
|
+ final int minY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(this.world);
|
|
+ final int maxY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(this.world);
|
|
+
|
|
ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Unloading poi chunk off-main");
|
|
- for (int section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
|
|
- final long sectionPos = SectionPos.asLong(chunkX, section, chunkZ);
|
|
+ for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
|
|
+ final long sectionPos = SectionPos.asLong(chunkX, sectionY, chunkZ);
|
|
this.updateDistanceTracking(sectionPos);
|
|
}
|
|
}
|
|
@@ -139,8 +142,12 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
|
|
public final void moonrise$loadInPoiChunk(final ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk poiChunk) {
|
|
final int chunkX = poiChunk.chunkX;
|
|
final int chunkZ = poiChunk.chunkZ;
|
|
+
|
|
+ final int minY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(this.world);
|
|
+ final int maxY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(this.world);
|
|
+
|
|
ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Loading poi chunk off-main");
|
|
- for (int sectionY = this.levelHeightAccessor.getMinSection(); sectionY < this.levelHeightAccessor.getMaxSection(); ++sectionY) {
|
|
+ for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
|
|
final PoiSection section = poiChunk.getSection(sectionY);
|
|
if (section != null && !((ca.spottedleaf.moonrise.patches.chunk_system.level.poi.ChunkSystemPoiSection)section).moonrise$isEmpty()) {
|
|
this.onSectionLoad(SectionPos.asLong(chunkX, sectionY, chunkZ));
|
|
@@ -316,8 +323,10 @@ public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> im
|
|
}
|
|
|
|
public int sectionsToVillage(SectionPos pos) {
|
|
- this.villageDistanceTracker.propagateUpdates(); // Paper - rewrite chunk system
|
|
- return convertBetweenLevels(this.villageDistanceTracker.getLevel(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionKey(pos))); // Paper - rewrite chunk system
|
|
+ // Paper start - rewrite chunk system
|
|
+ this.villageDistanceTracker.propagateUpdates();
|
|
+ return convertBetweenLevels(this.villageDistanceTracker.getLevel(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionKey(pos)));
|
|
+ // Paper end - rewrite chunk system
|
|
}
|
|
|
|
boolean isVillageCenter(long pos) {
|
|
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
|
|
index f6f0d7c21ee81ff33d4af350c4d39aadfbe140df..712cbfc100e8aaf612d1d651dae64f57f892a768 100644
|
|
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
|
|
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
|
|
@@ -31,7 +31,7 @@ public class PoiSection implements ca.spottedleaf.moonrise.patches.chunk_system.
|
|
private boolean isValid;
|
|
|
|
// Paper start - rewrite chunk system
|
|
- private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);;
|
|
+ private final Optional<PoiSection> noAllocOptional = Optional.of((PoiSection)(Object)this);
|
|
|
|
@Override
|
|
public final boolean moonrise$isEmpty() {
|
|
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
|
index 332dc7e6bdfb5b3741764d4877185a2e86a982f8..40fe47c7c145587ac81f0f15c237ed72ea9c094d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/Level.java
|
|
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
|
@@ -264,26 +264,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
// Paper start - optimise collisions
|
|
- private final int minSection;
|
|
- private final int maxSection;
|
|
-
|
|
- @Override
|
|
- public final int moonrise$getMinSection() {
|
|
- return this.minSection;
|
|
- }
|
|
-
|
|
- @Override
|
|
- public final int moonrise$getMaxSection() {
|
|
- return this.maxSection;
|
|
- }
|
|
-
|
|
/**
|
|
* Route to faster lookup.
|
|
* See {@link EntityGetter#isUnobstructed(Entity, VoxelShape)} for expected behavior
|
|
* @author Spottedleaf
|
|
*/
|
|
@Override
|
|
- public final boolean isUnobstructed(final Entity entity) {
|
|
+ public boolean isUnobstructed(final Entity entity) {
|
|
final AABB boundingBox = entity.getBoundingBox();
|
|
if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isEmpty(boundingBox)) {
|
|
return false;
|
|
@@ -313,7 +300,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
final Vec3 to = clipContext.getTo();
|
|
final Vec3 from = clipContext.getFrom();
|
|
|
|
- return net.minecraft.world.phys.BlockHitResult.miss(to, Direction.getNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
|
|
+ return net.minecraft.world.phys.BlockHitResult.miss(to, Direction.getApproximateNearest(from.x - to.x, from.y - to.y, from.z - to.z), BlockPos.containing(to.x, to.y, to.z));
|
|
}
|
|
|
|
private static final FluidState AIR_FLUIDSTATE = Fluids.EMPTY.defaultFluidState();
|
|
@@ -367,7 +354,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
int lastChunkY = Integer.MIN_VALUE;
|
|
int lastChunkZ = Integer.MIN_VALUE;
|
|
|
|
- final int minSection = ((ca.spottedleaf.moonrise.patches.collisions.world.CollisionLevel)level).moonrise$getMinSection();
|
|
+ final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level);
|
|
|
|
for (;;) {
|
|
currPos.set(currX, currY, currZ);
|
|
@@ -450,7 +437,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
* @author Spottedleaf
|
|
*/
|
|
@Override
|
|
- public final net.minecraft.world.phys.BlockHitResult clip(final ClipContext clipContext) {
|
|
+ public net.minecraft.world.phys.BlockHitResult clip(final ClipContext clipContext) {
|
|
// can only do this in this class, as not everything that implements BlockGetter can retrieve chunks
|
|
return fastClip(clipContext.getFrom(), clipContext.getTo(), (Level)(Object)this, clipContext);
|
|
}
|
|
@@ -460,7 +447,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
* @author Spottedleaf
|
|
*/
|
|
@Override
|
|
- public final boolean collidesWithSuffocatingBlock(final Entity entity, final AABB box) {
|
|
+ public boolean collidesWithSuffocatingBlock(final Entity entity, final AABB box) {
|
|
return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisionsForBlocksOrWorldBorder((Level)(Object)this, entity, box, null, null,
|
|
ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_ONLY,
|
|
(final BlockState state, final BlockPos pos) -> {
|
|
@@ -486,8 +473,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
* @author Spottedleaf
|
|
*/
|
|
@Override
|
|
- public final java.util.Optional<net.minecraft.world.phys.Vec3> findFreePosition(final Entity entity, final VoxelShape boundsShape, final Vec3 fromPosition,
|
|
- final double rangeX, final double rangeY, final double rangeZ) {
|
|
+ public java.util.Optional<net.minecraft.world.phys.Vec3> findFreePosition(final Entity entity, final VoxelShape boundsShape, final Vec3 fromPosition,
|
|
+ final double rangeX, final double rangeY, final double rangeZ) {
|
|
if (boundsShape.isEmpty()) {
|
|
return java.util.Optional.empty();
|
|
}
|
|
@@ -546,103 +533,139 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
* @author Spottedleaf
|
|
*/
|
|
@Override
|
|
- public final java.util.Optional<net.minecraft.core.BlockPos> findSupportingBlock(final Entity entity, final AABB aabb) {
|
|
+ public java.util.Optional<net.minecraft.core.BlockPos> findSupportingBlock(final Entity entity, final AABB aabb) {
|
|
+ final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection((Level)(Object)this);
|
|
+
|
|
final int minBlockX = Mth.floor(aabb.minX - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) - 1;
|
|
final int maxBlockX = Mth.floor(aabb.maxX + ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) + 1;
|
|
|
|
- final int minBlockY = Mth.floor(aabb.minY - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) - 1;
|
|
- final int maxBlockY = Mth.floor(aabb.maxY + ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) + 1;
|
|
+ final int minBlockY = Math.max((minSection << 4) - 1, Mth.floor(aabb.minY - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) - 1);
|
|
+ final int maxBlockY = Math.min((ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection((Level)(Object)this) << 4) + 16, Mth.floor(aabb.maxY + ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) + 1);
|
|
|
|
final int minBlockZ = Mth.floor(aabb.minZ - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) - 1;
|
|
final int maxBlockZ = Mth.floor(aabb.maxZ + ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) + 1;
|
|
|
|
- ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.LazyEntityCollisionContext collisionContext = null;
|
|
-
|
|
- final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
|
+ final BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos();
|
|
+ final ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.LazyEntityCollisionContext collisionShape = new ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.LazyEntityCollisionContext(entity);
|
|
BlockPos selected = null;
|
|
double selectedDistance = Double.MAX_VALUE;
|
|
-
|
|
final Vec3 entityPos = entity.position();
|
|
|
|
- LevelChunk lastChunk = null;
|
|
- int lastChunkX = Integer.MIN_VALUE;
|
|
- int lastChunkZ = Integer.MIN_VALUE;
|
|
+ // special cases:
|
|
+ if (minBlockY > maxBlockY) {
|
|
+ // no point in checking
|
|
+ return java.util.Optional.empty();
|
|
+ }
|
|
|
|
- final ChunkSource chunkSource = this.getChunkSource();
|
|
+ final int minChunkX = minBlockX >> 4;
|
|
+ final int maxChunkX = maxBlockX >> 4;
|
|
|
|
- for (int currZ = minBlockZ; currZ <= maxBlockZ; ++currZ) {
|
|
- pos.setZ(currZ);
|
|
- for (int currX = minBlockX; currX <= maxBlockX; ++currX) {
|
|
- pos.setX(currX);
|
|
+ final int minChunkY = minBlockY >> 4;
|
|
+ final int maxChunkY = maxBlockY >> 4;
|
|
|
|
- final int newChunkX = currX >> 4;
|
|
- final int newChunkZ = currZ >> 4;
|
|
+ final int minChunkZ = minBlockZ >> 4;
|
|
+ final int maxChunkZ = maxBlockZ >> 4;
|
|
|
|
- if (((newChunkX ^ lastChunkX) | (newChunkZ ^ lastChunkZ)) != 0) {
|
|
- lastChunkX = newChunkX;
|
|
- lastChunkZ = newChunkZ;
|
|
- lastChunk = (LevelChunk)chunkSource.getChunk(newChunkX, newChunkZ, ChunkStatus.FULL, false);
|
|
- }
|
|
+ final ChunkSource chunkSource = this.getChunkSource();
|
|
|
|
- if (lastChunk == null) {
|
|
+ for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
|
+ for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
|
|
+ final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, false);
|
|
+
|
|
+ if (chunk == null) {
|
|
continue;
|
|
}
|
|
- for (int currY = minBlockY; currY <= maxBlockY; ++currY) {
|
|
- int edgeCount = ((currX == minBlockX || currX == maxBlockX) ? 1 : 0) +
|
|
- ((currY == minBlockY || currY == maxBlockY) ? 1 : 0) +
|
|
- ((currZ == minBlockZ || currZ == maxBlockZ) ? 1 : 0);
|
|
- if (edgeCount == 3) {
|
|
- continue;
|
|
- }
|
|
|
|
- pos.setY(currY);
|
|
+ final net.minecraft.world.level.chunk.LevelChunkSection[] sections = chunk.getSections();
|
|
|
|
- final double distance = pos.distToCenterSqr(entityPos);
|
|
- if (distance > selectedDistance || (distance == selectedDistance && selected.compareTo(pos) >= 0)) {
|
|
+ // bound y
|
|
+ for (int currChunkY = minChunkY; currChunkY <= maxChunkY; ++currChunkY) {
|
|
+ final int sectionIdx = currChunkY - minSection;
|
|
+ if (sectionIdx < 0 || sectionIdx >= sections.length) {
|
|
continue;
|
|
}
|
|
-
|
|
- final BlockState state = ((ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk)lastChunk).moonrise$getBlock(currX, currY, currZ);
|
|
- if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)state).moonrise$emptyCollisionShape()) {
|
|
+ final net.minecraft.world.level.chunk.LevelChunkSection section = sections[sectionIdx];
|
|
+ if (section.hasOnlyAir()) {
|
|
+ // empty
|
|
continue;
|
|
}
|
|
|
|
- VoxelShape blockCollision = ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)state).moonrise$getConstantCollisionShape();
|
|
-
|
|
- if ((edgeCount != 1 || state.hasLargeCollisionShape()) && (edgeCount != 2 || state.getBlock() == Blocks.MOVING_PISTON)) {
|
|
- if (collisionContext == null) {
|
|
- collisionContext = new ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.LazyEntityCollisionContext(entity);
|
|
- }
|
|
-
|
|
- if (blockCollision == null) {
|
|
- blockCollision = state.getCollisionShape((Level)(Object)this, pos, collisionContext);
|
|
- }
|
|
-
|
|
- if (blockCollision.isEmpty()) {
|
|
- continue;
|
|
- }
|
|
-
|
|
- // avoid VoxelShape#move by shifting the entity collision shape instead
|
|
- final AABB shiftedAABB = aabb.move(-(double)currX, -(double)currY, -(double)currZ);
|
|
-
|
|
- final AABB singleAABB = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
|
|
- if (singleAABB != null) {
|
|
- if (!ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersect(singleAABB, shiftedAABB)) {
|
|
- continue;
|
|
+ final boolean hasSpecial = ((ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection)section).moonrise$hasSpecialCollidingBlocks();
|
|
+ final int sectionAdjust = !hasSpecial ? 1 : 0;
|
|
+
|
|
+ final net.minecraft.world.level.chunk.PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.states;
|
|
+
|
|
+ final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) + sectionAdjust : 0;
|
|
+ final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) - sectionAdjust : 15;
|
|
+ final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) + sectionAdjust : 0;
|
|
+ final int maxZIterate = currChunkZ == maxChunkZ ? (maxBlockZ & 15) - sectionAdjust : 15;
|
|
+ final int minYIterate = currChunkY == minChunkY ? (minBlockY & 15) + sectionAdjust : 0;
|
|
+ final int maxYIterate = currChunkY == maxChunkY ? (maxBlockY & 15) - sectionAdjust : 15;
|
|
+
|
|
+ for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
|
|
+ final int blockY = currY | (currChunkY << 4);
|
|
+ mutablePos.setY(blockY);
|
|
+ for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) {
|
|
+ final int blockZ = currZ | (currChunkZ << 4);
|
|
+ mutablePos.setZ(blockZ);
|
|
+ for (int currX = minXIterate; currX <= maxXIterate; ++currX) {
|
|
+ final int localBlockIndex = (currX) | (currZ << 4) | ((currY) << 8);
|
|
+ final int blockX = currX | (currChunkX << 4);
|
|
+ mutablePos.setX(blockX);
|
|
+
|
|
+ final int edgeCount = hasSpecial ? ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
|
|
+ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
|
|
+ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0) : 0;
|
|
+ if (edgeCount == 3) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ final double distance = mutablePos.distToCenterSqr(entityPos);
|
|
+ if (distance > selectedDistance || (distance == selectedDistance && selected.compareTo(mutablePos) >= 0)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ final BlockState blockData = blocks.get(localBlockIndex);
|
|
+
|
|
+ if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)blockData).moonrise$emptyContextCollisionShape()) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ VoxelShape blockCollision = ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)blockData).moonrise$getConstantContextCollisionShape();
|
|
+
|
|
+ if (edgeCount == 0 || ((edgeCount != 1 || blockData.hasLargeCollisionShape()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON))) {
|
|
+ if (blockCollision == null) {
|
|
+ blockCollision = blockData.getCollisionShape((Level)(Object)this, mutablePos, collisionShape);
|
|
+
|
|
+ if (blockCollision.isEmpty()) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // avoid VoxelShape#move by shifting the entity collision shape instead
|
|
+ final AABB shiftedAABB = aabb.move(-(double)blockX, -(double)blockY, -(double)blockZ);
|
|
+
|
|
+ final AABB singleAABB = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)blockCollision).moonrise$getSingleAABBRepresentation();
|
|
+ if (singleAABB != null) {
|
|
+ if (!ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersect(singleAABB, shiftedAABB)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ selected = mutablePos.immutable();
|
|
+ selectedDistance = distance;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (!ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersectNoEmpty(blockCollision, shiftedAABB)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ selected = mutablePos.immutable();
|
|
+ selectedDistance = distance;
|
|
+ continue;
|
|
+ }
|
|
}
|
|
-
|
|
- selected = pos.immutable();
|
|
- selectedDistance = distance;
|
|
- continue;
|
|
}
|
|
-
|
|
- if (!ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.voxelShapeIntersectNoEmpty(blockCollision, shiftedAABB)) {
|
|
- continue;
|
|
- }
|
|
-
|
|
- selected = pos.immutable();
|
|
- selectedDistance = distance;
|
|
- continue;
|
|
}
|
|
}
|
|
}
|
|
@@ -651,6 +674,74 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
return java.util.Optional.ofNullable(selected);
|
|
}
|
|
// Paper end - optimise collisions
|
|
+ // Paper start - getblock optimisations - cache world height/sections
|
|
+ private final int minY;
|
|
+ private final int height;
|
|
+ private final int maxY;
|
|
+ private final int minSectionY;
|
|
+ private final int maxSectionY;
|
|
+ private final int sectionsCount;
|
|
+
|
|
+ @Override
|
|
+ public int getMinY() {
|
|
+ return this.minY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getHeight() {
|
|
+ return this.height;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getMaxY() {
|
|
+ return this.maxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getSectionsCount() {
|
|
+ return this.sectionsCount;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getMinSectionY() {
|
|
+ return this.minSectionY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getMaxSectionY() {
|
|
+ return this.maxSectionY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isInsideBuildHeight(final int blockY) {
|
|
+ return blockY >= this.minY && blockY <= this.maxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isOutsideBuildHeight(final BlockPos pos) {
|
|
+ return this.isOutsideBuildHeight(pos.getY());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean isOutsideBuildHeight(final int blockY) {
|
|
+ return blockY < this.minY || blockY > this.maxY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getSectionIndex(final int blockY) {
|
|
+ return (blockY >> 4) - this.minSectionY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getSectionIndexFromSectionY(final int sectionY) {
|
|
+ return sectionY - this.minSectionY;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int getSectionYFromSectionIndex(final int sectionIdx) {
|
|
+ return sectionIdx + this.minSectionY;
|
|
+ }
|
|
+ // Paper end - getblock optimisations - cache world height/sections
|
|
// Paper start - optimise random ticking
|
|
@Override
|
|
public abstract Holder<Biome> getUncachedNoiseBiome(final int x, final int y, final int z);
|
|
@@ -669,6 +760,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
// Paper end - optimise random ticking
|
|
|
|
protected Level(WritableLevelData worlddatamutable, ResourceKey<Level> resourcekey, RegistryAccess iregistrycustom, Holder<DimensionType> holder, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function<org.spigotmc.SpigotWorldConfig, io.papermc.paper.configuration.WorldConfiguration> paperWorldConfigCreator) { // Paper - create paper world config
|
|
+ // Paper start - getblock optimisations - cache world height/sections
|
|
+ final DimensionType dimType = holder.value();
|
|
+ this.minY = dimType.minY();
|
|
+ this.height = dimType.height();
|
|
+ this.maxY = this.minY + this.height - 1;
|
|
+ this.minSectionY = this.minY >> 4;
|
|
+ this.maxSectionY = this.maxY >> 4;
|
|
+ this.sectionsCount = this.maxSectionY - this.minSectionY + 1;
|
|
+ // Paper end - getblock optimisations - cache world height/sections
|
|
this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot
|
|
this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config
|
|
this.generator = gen;
|
|
@@ -1573,7 +1673,16 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
|
if (slices == null) {
|
|
return new org.bukkit.entity.Entity[0];
|
|
}
|
|
- return slices.getChunkEntities();
|
|
+
|
|
+ List<org.bukkit.entity.Entity> ret = new java.util.ArrayList<>();
|
|
+ for (Entity entity : slices.getAllEntities()) {
|
|
+ org.bukkit.entity.Entity bukkit = entity.getBukkitEntity();
|
|
+ if (bukkit != null && bukkit.isValid()) {
|
|
+ ret.add(bukkit);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret.toArray(new org.bukkit.entity.Entity[0]);
|
|
}
|
|
// Paper end - rewrite chunk system
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java
|
|
index 01352cc83b25eb0e30b7e0ff521fc7c1b3d5155b..90f8360f547ce709fd13ee34f8e67d8bfa94b498 100644
|
|
--- a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java
|
|
+++ b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java
|
|
@@ -98,8 +98,7 @@ public class BiomeManager {
|
|
}
|
|
|
|
private static double getFiddle(long l) {
|
|
- double d = (double)Math.floorMod(l >> 24, 1024) / 1024.0;
|
|
- return (d - 0.5) * 0.9;
|
|
+ return (double)(((l >> 24) & (1024 - 1)) - (1024/2)) * (0.9 / 1024.0); // Paper - avoid floorMod, fp division, and fp subtraction
|
|
}
|
|
|
|
public interface NoiseBiomeSource {
|
|
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
|
index a4b4fd83d201fff005c738c84fa5c1bc55d670bd..8631655a181735df53f8a02c9eb98f0cc13f55bb 100644
|
|
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
|
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
|
@@ -838,18 +838,12 @@ public abstract class BlockBehaviour implements FeatureElement {
|
|
private int lightBlock;
|
|
|
|
// Paper start - rewrite chunk system
|
|
- private int opacityIfCached;
|
|
private boolean isConditionallyFullOpaque;
|
|
|
|
@Override
|
|
public final boolean starlight$isConditionallyFullOpaque() {
|
|
return this.isConditionallyFullOpaque;
|
|
}
|
|
-
|
|
- @Override
|
|
- public final int starlight$getOpacityIfCached() {
|
|
- return this.opacityIfCached;
|
|
- }
|
|
// Paper end - rewrite chunk system
|
|
// Paper start - optimise collisions
|
|
private static final int RANDOM_OFFSET = 704237939;
|
|
@@ -859,16 +853,22 @@ public abstract class BlockBehaviour implements FeatureElement {
|
|
private final int id2 = it.unimi.dsi.fastutil.HashCommon.murmurHash3(it.unimi.dsi.fastutil.HashCommon.murmurHash3(ID_GENERATOR.getAndIncrement() + RANDOM_OFFSET) + RANDOM_OFFSET);
|
|
private boolean occludesFullBlock;
|
|
private boolean emptyCollisionShape;
|
|
+ private boolean emptyConstantCollisionShape;
|
|
private VoxelShape constantCollisionShape;
|
|
- private AABB constantAABBCollision;
|
|
|
|
- private static void initCaches(final VoxelShape shape) {
|
|
+ private static void initCaches(final VoxelShape shape, final boolean neighbours) {
|
|
((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape).moonrise$isFullBlock();
|
|
((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape).moonrise$occludesFullBlock();
|
|
shape.toAabbs();
|
|
if (!shape.isEmpty()) {
|
|
shape.bounds();
|
|
}
|
|
+ if (neighbours) {
|
|
+ for (final Direction direction : DIRECTIONS_CACHED) {
|
|
+ initCaches(((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)shape).moonrise$getFaceShapeClamped(direction), false);
|
|
+ initCaches(shape.getFaceShape(direction), false);
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
@Override
|
|
@@ -886,6 +886,11 @@ public abstract class BlockBehaviour implements FeatureElement {
|
|
return this.emptyCollisionShape;
|
|
}
|
|
|
|
+ @Override
|
|
+ public final boolean moonrise$emptyContextCollisionShape() {
|
|
+ return this.emptyConstantCollisionShape;
|
|
+ }
|
|
+
|
|
@Override
|
|
public final int moonrise$uniqueId1() {
|
|
return this.id1;
|
|
@@ -897,14 +902,9 @@ public abstract class BlockBehaviour implements FeatureElement {
|
|
}
|
|
|
|
@Override
|
|
- public final VoxelShape moonrise$getConstantCollisionShape() {
|
|
+ public final VoxelShape moonrise$getConstantContextCollisionShape() {
|
|
return this.constantCollisionShape;
|
|
}
|
|
-
|
|
- @Override
|
|
- public final AABB moonrise$getConstantCollisionAABB() {
|
|
- return this.constantAABBCollision;
|
|
- }
|
|
// Paper end - optimise collisions
|
|
|
|
protected BlockStateBase(Block block, Reference2ObjectArrayMap<Property<?>, Comparable<?>> propertyMap, MapCodec<BlockState> codec) {
|
|
@@ -993,39 +993,37 @@ public abstract class BlockBehaviour implements FeatureElement {
|
|
this.lightBlock = ((Block) this.owner).getLightBlock(this.asState());
|
|
// Paper start - rewrite chunk system
|
|
this.isConditionallyFullOpaque = this.canOcclude & this.useShapeForLightOcclusion;
|
|
- this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque ? -1 : this.cache.lightBlock;
|
|
// Paper end - rewrite chunk system
|
|
// Paper start - optimise collisions
|
|
if (this.cache != null) {
|
|
final VoxelShape collisionShape = this.cache.collisionShape;
|
|
try {
|
|
this.constantCollisionShape = this.getCollisionShape(null, null, null);
|
|
- this.constantAABBCollision = this.constantCollisionShape == null ? null : ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)this.constantCollisionShape).moonrise$getSingleAABBRepresentation();
|
|
} catch (final Throwable throwable) {
|
|
this.constantCollisionShape = null;
|
|
- this.constantAABBCollision = null;
|
|
}
|
|
this.occludesFullBlock = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)collisionShape).moonrise$occludesFullBlock();
|
|
this.emptyCollisionShape = collisionShape.isEmpty();
|
|
+ this.emptyConstantCollisionShape = this.constantCollisionShape != null && this.constantCollisionShape.isEmpty();
|
|
// init caches
|
|
- initCaches(collisionShape);
|
|
- if (collisionShape != Shapes.empty() && collisionShape != Shapes.block()) {
|
|
- for (final Direction direction : DIRECTIONS_CACHED) {
|
|
- // initialise the directional face shape cache as well
|
|
- final VoxelShape shape = Shapes.getFaceShape(collisionShape, direction);
|
|
- initCaches(shape);
|
|
- }
|
|
- }
|
|
- if (this.cache.occlusionShapes != null) {
|
|
- for (final VoxelShape shape : this.cache.occlusionShapes) {
|
|
- initCaches(shape);
|
|
- }
|
|
+ initCaches(collisionShape, true);
|
|
+ if (this.constantCollisionShape != null) {
|
|
+ initCaches(this.constantCollisionShape, true);
|
|
}
|
|
} else {
|
|
this.occludesFullBlock = false;
|
|
this.emptyCollisionShape = false;
|
|
+ this.emptyConstantCollisionShape = false;
|
|
this.constantCollisionShape = null;
|
|
- this.constantAABBCollision = null;
|
|
+ }
|
|
+
|
|
+ if (this.occlusionShape != null) {
|
|
+ initCaches(this.occlusionShape, true);
|
|
+ }
|
|
+ if (this.occlusionShapesByFace != null) {
|
|
+ for (final VoxelShape shape : this.occlusionShapesByFace) {
|
|
+ initCaches(shape, true);
|
|
+ }
|
|
}
|
|
// Paper end - optimise collisions
|
|
}
|
|
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 422b364764e0df16ca250b4939d7b226e69c0840..2df28ffc731bd77e0d7af3541cfd3741aa5af83b 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
|
|
@@ -15,7 +15,7 @@ import java.util.stream.Collectors;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.world.level.block.state.properties.Property;
|
|
|
|
-public abstract class StateHolder<O, S> {
|
|
+public abstract class StateHolder<O, S> implements ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccessStateHolder { // Paper - optimise blockstate property access
|
|
public static final String NAME_TAG = "Name";
|
|
public static final String PROPERTIES_TAG = "Properties";
|
|
public static final Function<Entry<Property<?>, Comparable<?>>, String> PROPERTY_ENTRY_TO_STRING_FUNCTION = new Function<Entry<Property<?>, Comparable<?>>, String>() {
|
|
@@ -34,14 +34,28 @@ public abstract class StateHolder<O, S> {
|
|
}
|
|
};
|
|
protected final O owner;
|
|
- private final Reference2ObjectArrayMap<Property<?>, Comparable<?>> values;
|
|
+ private Reference2ObjectArrayMap<Property<?>, Comparable<?>> values; // Paper - optimise blockstate property access - remove final
|
|
private Map<Property<?>, S[]> neighbours;
|
|
protected final MapCodec<S> propertiesCodec;
|
|
|
|
+ // Paper start - optimise blockstate property access
|
|
+ protected ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.util.ZeroCollidingReferenceStateTable<O, S> optimisedTable;
|
|
+ protected final long tableIndex;
|
|
+
|
|
+ @Override
|
|
+ public final long moonrise$getTableIndex() {
|
|
+ return this.tableIndex;
|
|
+ }
|
|
+ // Paper end - optimise blockstate property access
|
|
+
|
|
protected StateHolder(O owner, Reference2ObjectArrayMap<Property<?>, Comparable<?>> propertyMap, MapCodec<S> codec) {
|
|
this.owner = owner;
|
|
this.values = propertyMap;
|
|
this.propertiesCodec = codec;
|
|
+ // Paper start - optimise blockstate property access
|
|
+ this.optimisedTable = new ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.util.ZeroCollidingReferenceStateTable<>(this.values.keySet());
|
|
+ this.tableIndex = this.optimisedTable.getIndex((StateHolder<O, S>)(Object)this);
|
|
+ // Paper end - optimise blockstate property access
|
|
}
|
|
|
|
public <T extends Comparable<T>> S cycle(Property<T> property) {
|
|
@@ -67,20 +81,21 @@ public abstract class StateHolder<O, S> {
|
|
}
|
|
|
|
public Collection<Property<?>> getProperties() {
|
|
- return Collections.unmodifiableCollection(this.values.keySet());
|
|
+ return this.optimisedTable.getProperties(); // Paper - optimise blockstate property access
|
|
}
|
|
|
|
public <T extends Comparable<T>> boolean hasProperty(Property<T> property) {
|
|
- return this.values.containsKey(property);
|
|
+ return property != null && this.optimisedTable.hasProperty(property); // Paper - optimise blockstate property access
|
|
}
|
|
|
|
public <T extends Comparable<T>> T getValue(Property<T> property) {
|
|
- Comparable<?> comparable = this.values.get(property);
|
|
- if (comparable == null) {
|
|
- throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner);
|
|
- } else {
|
|
- return property.getValueClass().cast(comparable);
|
|
+ // Paper start - optimise blockstate property access
|
|
+ final T ret = this.optimisedTable.get(this.tableIndex, property);
|
|
+ if (ret != null) {
|
|
+ return ret;
|
|
}
|
|
+ throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner);
|
|
+ // Paper end - optimise blockstate property access
|
|
}
|
|
|
|
public <T extends Comparable<T>> Optional<T> getOptionalValue(Property<T> property) {
|
|
@@ -93,22 +108,30 @@ public abstract class StateHolder<O, S> {
|
|
|
|
@Nullable
|
|
public <T extends Comparable<T>> T getNullableValue(Property<T> property) {
|
|
- Comparable<?> comparable = this.values.get(property);
|
|
- return comparable == null ? null : property.getValueClass().cast(comparable);
|
|
+ return property == null ? null : this.optimisedTable.get(this.tableIndex, property); // Paper - optimise blockstate property access
|
|
}
|
|
|
|
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 {
|
|
- return this.setValueInternal(property, value, comparable);
|
|
+ // Paper start - optimise blockstate property access
|
|
+ final S ret = this.optimisedTable.set(this.tableIndex, property, value);
|
|
+ if (ret != null) {
|
|
+ return ret;
|
|
}
|
|
+ throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner);
|
|
+ // Paper end - optimise blockstate property access
|
|
}
|
|
|
|
public <T extends Comparable<T>, V extends T> S trySetValue(Property<T> property, V value) {
|
|
- Comparable<?> comparable = this.values.get(property);
|
|
- return (S)(comparable == null ? this : this.setValueInternal(property, value, comparable));
|
|
+ // Paper start - optimise blockstate property access
|
|
+ if (property == null) {
|
|
+ return (S)(StateHolder<O, S>)(Object)this;
|
|
+ }
|
|
+ final S ret = this.optimisedTable.trySet(this.tableIndex, property, value, (S)(StateHolder<O, S>)(Object)this);
|
|
+ if (ret != null) {
|
|
+ return ret;
|
|
+ }
|
|
+ throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner);
|
|
+ // Paper end - optimise blockstate property access
|
|
}
|
|
|
|
private <T extends Comparable<T>, V extends T> S setValueInternal(Property<T> property, V newValue, Comparable<?> oldValue) {
|
|
@@ -125,18 +148,27 @@ public abstract class StateHolder<O, S> {
|
|
}
|
|
|
|
public void populateNeighbours(Map<Map<Property<?>, Comparable<?>>, S> states) {
|
|
- if (this.neighbours != null) {
|
|
- throw new IllegalStateException();
|
|
- } else {
|
|
- Map<Property<?>, S[]> map = new Reference2ObjectArrayMap<>(this.values.size());
|
|
+ // Paper start - optimise blockstate property access
|
|
+ final Map<Map<Property<?>, Comparable<?>>, S> map = states;
|
|
+ if (this.optimisedTable.isLoaded()) {
|
|
+ return;
|
|
+ }
|
|
+ this.optimisedTable.loadInTable(map);
|
|
|
|
- for (Entry<Property<?>, Comparable<?>> entry : this.values.entrySet()) {
|
|
- Property<?> property = entry.getKey();
|
|
- map.put(property, property.getPossibleValues().stream().map(value -> states.get(this.makeNeighbourValues(property, value))).toArray());
|
|
- }
|
|
+ // de-duplicate the tables
|
|
+ for (final Map.Entry<Map<Property<?>, Comparable<?>>, S> entry : map.entrySet()) {
|
|
+ final S value = entry.getValue();
|
|
+ ((StateHolder<O, S>)value).optimisedTable = this.optimisedTable;
|
|
+ }
|
|
|
|
- this.neighbours = map;
|
|
+ // remove values arrays
|
|
+ for (final Map.Entry<Map<Property<?>, Comparable<?>>, S> entry : map.entrySet()) {
|
|
+ final S value = entry.getValue();
|
|
+ ((StateHolder<O, S>)value).values = null;
|
|
}
|
|
+
|
|
+ return;
|
|
+ // Paper end optimise blockstate property access
|
|
}
|
|
|
|
private Map<Property<?>, Comparable<?>> makeNeighbourValues(Property<?> property, Comparable<?> value) {
|
|
@@ -146,7 +178,11 @@ public abstract class StateHolder<O, S> {
|
|
}
|
|
|
|
public Map<Property<?>, Comparable<?>> getValues() {
|
|
- return this.values;
|
|
+ // Paper start - optimise blockstate property access
|
|
+ ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.util.ZeroCollidingReferenceStateTable<O, S> table = this.optimisedTable;
|
|
+ // We have to use this.values until the table is loaded
|
|
+ return table.isLoaded() ? table.getMapView(this.tableIndex) : this.values;
|
|
+ // Paper end - optimise blockstate property access
|
|
}
|
|
|
|
protected static <O, S extends StateHolder<O, S>> Codec<S> codec(Codec<O> codec, Function<O, S> ownerToStateFunction) {
|
|
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 ea76aa490358e9e1d13350ba0ea246ec2c423894..98058505d36baf74008da08339afc196713b14a7 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
|
|
@@ -3,13 +3,23 @@ package net.minecraft.world.level.block.state.properties;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
|
|
-public final class BooleanProperty extends Property<Boolean> {
|
|
+public final class BooleanProperty extends Property<Boolean> implements ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccess<Boolean> { // Paper - optimise blockstate property access
|
|
private static final List<Boolean> VALUES = List.of(true, false);
|
|
private static final int TRUE_INDEX = 0;
|
|
private static final int FALSE_INDEX = 1;
|
|
|
|
+ // Paper start - optimise blockstate property access
|
|
+ private static final Boolean[] BY_ID = new Boolean[]{ Boolean.FALSE, Boolean.TRUE };
|
|
+
|
|
+ @Override
|
|
+ public final int moonrise$getIdFor(final Boolean value) {
|
|
+ return value.booleanValue() ? 1 : 0;
|
|
+ }
|
|
+ // Paper end - optimise blockstate property access
|
|
+
|
|
private BooleanProperty(String name) {
|
|
super(name, Boolean.class);
|
|
+ this.moonrise$setById(BY_ID); // Paper - optimise blockstate property access
|
|
}
|
|
|
|
@Override
|
|
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 85a197232be9377c0313ec00e8f935551e2c60e0..30b2fce9e47ffcc3de1542b1d0f073f5640127a7 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
|
|
@@ -10,11 +10,39 @@ import java.util.function.Predicate;
|
|
import java.util.stream.Collectors;
|
|
import net.minecraft.util.StringRepresentable;
|
|
|
|
-public final class EnumProperty<T extends Enum<T> & StringRepresentable> extends Property<T> {
|
|
+public final class EnumProperty<T extends Enum<T> & StringRepresentable> extends Property<T> implements ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccess<T> { // Paper - optimise blockstate property access
|
|
private final List<T> values;
|
|
private final Map<String, T> names;
|
|
private final int[] ordinalToIndex;
|
|
|
|
+ // Paper start - optimise blockstate property access
|
|
+ private int[] idLookupTable;
|
|
+
|
|
+ @Override
|
|
+ public final int moonrise$getIdFor(final T value) {
|
|
+ final Class<T> target = this.getValueClass();
|
|
+ return ((value.getClass() != target && value.getDeclaringClass() != target)) ? -1 : this.idLookupTable[value.ordinal()];
|
|
+ }
|
|
+
|
|
+ private void init() {
|
|
+ final java.util.Collection<T> values = this.getPossibleValues();
|
|
+ final Class<T> clazz = this.getValueClass();
|
|
+
|
|
+ int id = 0;
|
|
+ this.idLookupTable = new int[clazz.getEnumConstants().length];
|
|
+ Arrays.fill(this.idLookupTable, -1);
|
|
+ final T[] byId = (T[])java.lang.reflect.Array.newInstance(clazz, values.size());
|
|
+
|
|
+ for (final T value : values) {
|
|
+ final int valueId = id++;
|
|
+ this.idLookupTable[value.ordinal()] = valueId;
|
|
+ byId[valueId] = value;
|
|
+ }
|
|
+
|
|
+ this.moonrise$setById(byId);
|
|
+ }
|
|
+ // Paper end - optimise blockstate property access
|
|
+
|
|
private EnumProperty(String name, Class<T> type, List<T> values) {
|
|
super(name, type);
|
|
if (values.isEmpty()) {
|
|
@@ -37,6 +65,7 @@ public final class EnumProperty<T extends Enum<T> & StringRepresentable> extends
|
|
|
|
this.names = builder.buildOrThrow();
|
|
}
|
|
+ this.init(); // Paper - optimise blockstate property access
|
|
}
|
|
|
|
@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 55a87592a99105dbf57b26fb6ccba695295fce24..986365acc9983331a7982ea2e1eac2b0efe1506d 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
|
|
@@ -5,11 +5,33 @@ import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.stream.IntStream;
|
|
|
|
-public final class IntegerProperty extends Property<Integer> {
|
|
+public final class IntegerProperty extends Property<Integer> implements ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccess<Integer> { // Paper - optimise blockstate property access
|
|
private final IntImmutableList values;
|
|
public final int min;
|
|
public final int max;
|
|
|
|
+ // Paper start - optimise blockstate property access
|
|
+ @Override
|
|
+ public final int moonrise$getIdFor(final Integer value) {
|
|
+ final int val = value.intValue();
|
|
+ final int ret = val - this.min;
|
|
+
|
|
+ return ret | ((this.max - ret) >> 31);
|
|
+ }
|
|
+
|
|
+ private void init() {
|
|
+ final int min = this.min;
|
|
+ final int max = this.max;
|
|
+
|
|
+ final Integer[] byId = new Integer[max - min + 1];
|
|
+ for (int i = min; i <= max; ++i) {
|
|
+ byId[i - min] = Integer.valueOf(i);
|
|
+ }
|
|
+
|
|
+ this.moonrise$setById(byId);
|
|
+ }
|
|
+ // Paper end - optimise blockstate property access
|
|
+
|
|
private IntegerProperty(String name, int min, int max) {
|
|
super(name, Integer.class);
|
|
if (min < 0) {
|
|
@@ -21,6 +43,7 @@ public final class IntegerProperty extends Property<Integer> {
|
|
this.max = max;
|
|
this.values = IntImmutableList.toList(IntStream.range(min, max + 1));
|
|
}
|
|
+ this.init(); // Paper - optimise blockstate property access
|
|
}
|
|
|
|
@Override
|
|
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 fcf04c5c58ff35d38c5bf0df562ae2f8dc98a0ee..0b116160924300a9d62ad5948bfaf276f0386e4d 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
|
|
@@ -10,7 +10,7 @@ import java.util.stream.Stream;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.world.level.block.state.StateHolder;
|
|
|
|
-public abstract class Property<T extends Comparable<T>> {
|
|
+public abstract class Property<T extends Comparable<T>> implements ca.spottedleaf.moonrise.patches.blockstate_propertyaccess.PropertyAccess<T> { // Paper - optimise blockstate property access
|
|
private final Class<T> clazz;
|
|
private final String name;
|
|
@Nullable
|
|
@@ -24,9 +24,38 @@ public abstract class Property<T extends Comparable<T>> {
|
|
);
|
|
private final Codec<Property.Value<T>> valueCodec = this.codec.xmap(this::value, Property.Value::value);
|
|
|
|
+ // Paper start - optimise blockstate property access
|
|
+ private static final java.util.concurrent.atomic.AtomicInteger ID_GENERATOR = new java.util.concurrent.atomic.AtomicInteger();
|
|
+ private final int id;
|
|
+ private T[] byId;
|
|
+
|
|
+ @Override
|
|
+ public final int moonrise$getId() {
|
|
+ return this.id;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final T moonrise$getById(final int id) {
|
|
+ final T[] byId = this.byId;
|
|
+ return id < 0 || id >= byId.length ? null : this.byId[id];
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final void moonrise$setById(final T[] byId) {
|
|
+ if (this.byId != null) {
|
|
+ throw new IllegalStateException();
|
|
+ }
|
|
+ this.byId = byId;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public abstract int moonrise$getIdFor(final T value);
|
|
+ // Paper end - optimise blockstate property access
|
|
+
|
|
protected Property(String name, Class<T> type) {
|
|
this.clazz = type;
|
|
this.name = name;
|
|
+ this.id = ID_GENERATOR.getAndIncrement(); // Paper - optimise blockstate property access
|
|
}
|
|
|
|
public Property.Value<T> value(T value) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java b/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
|
|
index 98dbeaf8bde15940e5b5d5d1f13fd4bb32f0a10d..7beea075b5a7ef738a4ac0558b99f4c5708f2c4a 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
|
|
@@ -8,12 +8,19 @@ import net.minecraft.network.FriendlyByteBuf;
|
|
import net.minecraft.network.VarInt;
|
|
import net.minecraft.util.CrudeIncrementalIntIdentityHashBiMap;
|
|
|
|
-public class HashMapPalette<T> implements Palette<T> {
|
|
+public class HashMapPalette<T> implements Palette<T>, ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<T> { // Paper - optimise palette reads
|
|
private final IdMap<T> registry;
|
|
private final CrudeIncrementalIntIdentityHashBiMap<T> values;
|
|
private final PaletteResize<T> resizeHandler;
|
|
private final int bits;
|
|
|
|
+ // Paper start - optimise palette reads
|
|
+ @Override
|
|
+ public final T[] moonrise$getRawPalette(final ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T> container) {
|
|
+ return ((ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<T>)this.values).moonrise$getRawPalette(container);
|
|
+ }
|
|
+ // Paper end - optimise palette reads
|
|
+
|
|
public HashMapPalette(IdMap<T> idList, int bits, PaletteResize<T> listener, List<T> entries) {
|
|
this(idList, bits, listener);
|
|
entries.forEach(this.values::add);
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
index a61294befc2f855fcecb2336a2d5444ce60e0a3a..a0e51681731dc7b487d5b14ae0d44a881bd5cb09 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
|
@@ -54,7 +54,7 @@ import net.minecraft.world.ticks.LevelChunkTicks;
|
|
import net.minecraft.world.ticks.TickContainerAccess;
|
|
import org.slf4j.Logger;
|
|
|
|
-public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk, ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk, ca.spottedleaf.moonrise.patches.chunk_getblock.GetBlockChunk { // Paper - rewrite chunk system // Paper - get block chunk optimisation
|
|
+public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk, ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk, ca.spottedleaf.moonrise.patches.getblock.GetBlockChunk { // Paper - rewrite chunk system // Paper - get block chunk optimisation
|
|
|
|
static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final TickingBlockEntity NULL_TICKER = new TickingBlockEntity() {
|
|
@@ -688,7 +688,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
|
*/
|
|
org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this);
|
|
server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(bukkitChunk, this.needsDecoration));
|
|
- ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(this.locX, this.locZ).getEntityChunk().callEntitiesLoadEvent(); // Paper - rewrite chunk system
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesLoadEvent(this.level, this.chunkPos, ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(this.locX, this.locZ).getEntityChunk().getAllEntities()); // Paper - rewrite chunk system
|
|
|
|
if (this.needsDecoration) {
|
|
try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper
|
|
@@ -719,7 +719,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
|
public void unloadCallback() {
|
|
if (!this.loadedTicketLevel) { LOGGER.error("Double calling chunk unload!", new Throwable()); } // Paper
|
|
org.bukkit.Server server = this.level.getCraftServer();
|
|
- ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(this.locX, this.locZ).getEntityChunk().callEntitiesUnloadEvent(); // Paper - rewrite chunk system
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.callEntitiesUnloadEvent(this.level, this.chunkPos, ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(this.locX, this.locZ).getEntityChunk().getAllEntities()); // Paper - rewrite chunk system
|
|
org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this);
|
|
org.bukkit.event.world.ChunkUnloadEvent unloadEvent = new org.bukkit.event.world.ChunkUnloadEvent(bukkitChunk, true); // Paper - rewrite chunk system - force save to true so that mustNotSave is correctly set below
|
|
server.getPluginManager().callEvent(unloadEvent);
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
index 161211124f3f8390530af7ab21f3a0f1025209c5..4167ed830382c6a76bb281e9d753919925c6bd00 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
|
|
@@ -26,15 +26,17 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
private PalettedContainer<Holder<Biome>> biomes; // CraftBukkit - read/write
|
|
|
|
// Paper start - block counting
|
|
- private static final it.unimi.dsi.fastutil.ints.IntArrayList FULL_LIST = new it.unimi.dsi.fastutil.ints.IntArrayList(16*16*16);
|
|
+ private static final it.unimi.dsi.fastutil.shorts.ShortArrayList FULL_LIST = new it.unimi.dsi.fastutil.shorts.ShortArrayList(16*16*16);
|
|
static {
|
|
- for (int i = 0; i < (16*16*16); ++i) {
|
|
+ for (short i = 0; i < (16*16*16); ++i) {
|
|
FULL_LIST.add(i);
|
|
}
|
|
}
|
|
|
|
- private int specialCollidingBlocks;
|
|
- private final ca.spottedleaf.moonrise.common.list.IBlockDataList tickingBlocks = new ca.spottedleaf.moonrise.common.list.IBlockDataList();
|
|
+ private boolean isClient;
|
|
+ private static final short CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS = (short)9999;
|
|
+ private short specialCollidingBlocks;
|
|
+ private final ca.spottedleaf.moonrise.common.list.ShortList tickingBlocks = new ca.spottedleaf.moonrise.common.list.ShortList();
|
|
|
|
@Override
|
|
public final int moonrise$getSpecialCollidingBlocks() {
|
|
@@ -42,7 +44,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
}
|
|
|
|
@Override
|
|
- public final ca.spottedleaf.moonrise.common.list.IBlockDataList moonrise$getTickingBlockList() {
|
|
+ public final ca.spottedleaf.moonrise.common.list.ShortList moonrise$getTickingBlockList() {
|
|
return this.tickingBlocks;
|
|
}
|
|
// Paper end - block counting
|
|
@@ -86,6 +88,45 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
return this.setBlockState(x, y, z, state, true);
|
|
}
|
|
|
|
+ // Paper start - block counting
|
|
+ private void updateBlockCallback(final int x, final int y, final int z, final BlockState newState,
|
|
+ final BlockState oldState) {
|
|
+ if (oldState == newState) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (this.isClient) {
|
|
+ if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isSpecialCollidingBlock(newState)) {
|
|
+ this.specialCollidingBlocks = CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ final boolean isSpecialOld = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isSpecialCollidingBlock(oldState);
|
|
+ final boolean isSpecialNew = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isSpecialCollidingBlock(newState);
|
|
+ if (isSpecialOld != isSpecialNew) {
|
|
+ if (isSpecialOld) {
|
|
+ --this.specialCollidingBlocks;
|
|
+ } else {
|
|
+ ++this.specialCollidingBlocks;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final boolean oldTicking = oldState.isRandomlyTicking();
|
|
+ final boolean newTicking = newState.isRandomlyTicking();
|
|
+ if (oldTicking != newTicking) {
|
|
+ final ca.spottedleaf.moonrise.common.list.ShortList tickingBlocks = this.tickingBlocks;
|
|
+ final short position = (short)(x | (z << 4) | (y << (4+4)));
|
|
+
|
|
+ if (oldTicking) {
|
|
+ tickingBlocks.remove(position);
|
|
+ } else {
|
|
+ tickingBlocks.add(position);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Paper end - block counting
|
|
+
|
|
public BlockState setBlockState(int x, int y, int z, BlockState state, boolean lock) {
|
|
BlockState iblockdata1;
|
|
|
|
@@ -105,7 +146,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
}
|
|
}
|
|
|
|
- if (!fluid.isEmpty()) {
|
|
+ if (!!fluid.isRandomlyTicking()) { // Paper - block counting
|
|
--this.tickingFluidCount;
|
|
}
|
|
|
|
@@ -116,25 +157,11 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
}
|
|
}
|
|
|
|
- if (!fluid1.isEmpty()) {
|
|
+ if (!!fluid1.isRandomlyTicking()) { // Paper - block counting
|
|
++this.tickingFluidCount;
|
|
}
|
|
|
|
- // Paper start - block counting
|
|
- if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isSpecialCollidingBlock(iblockdata1)) {
|
|
- --this.specialCollidingBlocks;
|
|
- }
|
|
- if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isSpecialCollidingBlock(state)) {
|
|
- ++this.specialCollidingBlocks;
|
|
- }
|
|
-
|
|
- if (iblockdata1.isRandomlyTicking()) {
|
|
- this.tickingBlocks.remove(x, y, z);
|
|
- }
|
|
- if (state.isRandomlyTicking()) {
|
|
- this.tickingBlocks.add(x, y, z, state);
|
|
- }
|
|
- // Paper end - block counting
|
|
+ this.updateBlockCallback(x, y, z, state, iblockdata1); // Paper - block counting
|
|
|
|
return iblockdata1;
|
|
}
|
|
@@ -170,7 +197,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
final int paletteSize = palette.getSize();
|
|
final net.minecraft.util.BitStorage storage = data.storage();
|
|
|
|
- final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.ints.IntArrayList> counts;
|
|
+ final it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<it.unimi.dsi.fastutil.shorts.ShortArrayList> counts;
|
|
if (paletteSize == 1) {
|
|
counts = new it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap<>(1);
|
|
counts.put(0, FULL_LIST);
|
|
@@ -178,10 +205,10 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
counts = ((ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage)storage).moonrise$countEntries();
|
|
}
|
|
|
|
- for (final java.util.Iterator<it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry<it.unimi.dsi.fastutil.ints.IntArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
|
- final it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry<it.unimi.dsi.fastutil.ints.IntArrayList> entry = iterator.next();
|
|
+ for (final java.util.Iterator<it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry<it.unimi.dsi.fastutil.shorts.ShortArrayList>> iterator = counts.int2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
|
|
+ final it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry<it.unimi.dsi.fastutil.shorts.ShortArrayList> entry = iterator.next();
|
|
final int paletteIdx = entry.getIntKey();
|
|
- final it.unimi.dsi.fastutil.ints.IntArrayList coordinates = entry.getValue();
|
|
+ final it.unimi.dsi.fastutil.shorts.ShortArrayList coordinates = entry.getValue();
|
|
final int paletteCount = coordinates.size();
|
|
|
|
final BlockState state = palette.valueFor(paletteIdx);
|
|
@@ -191,25 +218,30 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
}
|
|
|
|
if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.isSpecialCollidingBlock(state)) {
|
|
- this.specialCollidingBlocks += paletteCount;
|
|
+ this.specialCollidingBlocks += (short)paletteCount;
|
|
}
|
|
- this.nonEmptyBlockCount += paletteCount;
|
|
+ this.nonEmptyBlockCount += (short)paletteCount;
|
|
if (state.isRandomlyTicking()) {
|
|
- this.tickingBlockCount += paletteCount;
|
|
- final int[] raw = coordinates.elements();
|
|
+ this.tickingBlockCount += (short)paletteCount;
|
|
+ final short[] raw = coordinates.elements();
|
|
+ final int rawLen = raw.length;
|
|
+
|
|
+ final ca.spottedleaf.moonrise.common.list.ShortList tickingBlocks = this.tickingBlocks;
|
|
+
|
|
+ tickingBlocks.setMinCapacity(Math.min((rawLen + tickingBlocks.size()) * 3 / 2, 16*16*16));
|
|
|
|
java.util.Objects.checkFromToIndex(0, paletteCount, raw.length);
|
|
for (int i = 0; i < paletteCount; ++i) {
|
|
- this.tickingBlocks.add(raw[i], state);
|
|
+ tickingBlocks.add(raw[i]);
|
|
}
|
|
}
|
|
|
|
final FluidState fluid = state.getFluidState();
|
|
|
|
if (!fluid.isEmpty()) {
|
|
- //this.nonEmptyBlockCount += count; // fix vanilla bug: make non empty block count correct
|
|
+ //this.nonEmptyBlockCount += count; // fix vanilla bug: make non-empty block count correct
|
|
if (fluid.isRandomlyTicking()) {
|
|
- this.tickingFluidCount += paletteCount;
|
|
+ this.tickingFluidCount += (short)paletteCount;
|
|
}
|
|
}
|
|
}
|
|
@@ -232,7 +264,11 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
|
|
|
datapaletteblock.read(buf);
|
|
this.biomes = datapaletteblock;
|
|
- this.recalcBlockCounts(); // Paper - block counting
|
|
+ // Paper start - block counting
|
|
+ this.isClient = true;
|
|
+ // force has special colliding blocks to be true
|
|
+ this.specialCollidingBlocks = this.nonEmptyBlockCount != (short)0 && this.maybeHas(ca.spottedleaf.moonrise.patches.collisions.CollisionUtil::isSpecialCollidingBlock) ? CLIENT_FORCED_SPECIAL_COLLIDING_BLOCKS : (short)0;
|
|
+ // Paper end - block counting
|
|
}
|
|
|
|
public void readBiomes(FriendlyByteBuf buf) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/LinearPalette.java b/src/main/java/net/minecraft/world/level/chunk/LinearPalette.java
|
|
index bc4d9452bbeb05a691fd285603e49491f41d3ad2..f8d9892970c9092f7cc84434d4fbf34354ce1195 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/LinearPalette.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/LinearPalette.java
|
|
@@ -7,13 +7,20 @@ import net.minecraft.network.FriendlyByteBuf;
|
|
import net.minecraft.network.VarInt;
|
|
import org.apache.commons.lang3.Validate;
|
|
|
|
-public class LinearPalette<T> implements Palette<T> {
|
|
+public class LinearPalette<T> implements Palette<T>, ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<T> { // Paper - optimise palette reads
|
|
private final IdMap<T> registry;
|
|
private final T[] values;
|
|
private final PaletteResize<T> resizeHandler;
|
|
private final int bits;
|
|
private int size;
|
|
|
|
+ // Paper start - optimise palette reads
|
|
+ @Override
|
|
+ public final T[] moonrise$getRawPalette(final ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T> container) {
|
|
+ return this.values;
|
|
+ }
|
|
+ // Paper end - optimise palette reads
|
|
+
|
|
private LinearPalette(IdMap<T> idList, int bits, PaletteResize<T> listener, List<T> list) {
|
|
this.registry = idList;
|
|
this.values = (T[])(new Object[1 << bits]);
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/Palette.java b/src/main/java/net/minecraft/world/level/chunk/Palette.java
|
|
index b8922e4a13df535cdc5701e893a6e460b33ff90d..100807f8b8337f56f49cdb818ccc75be2f08ecd1 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/Palette.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/Palette.java
|
|
@@ -5,7 +5,7 @@ import java.util.function.Predicate;
|
|
import net.minecraft.core.IdMap;
|
|
import net.minecraft.network.FriendlyByteBuf;
|
|
|
|
-public interface Palette<T> {
|
|
+public interface Palette<T> extends ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<T> { // Paper - optimise palette reads
|
|
int idFor(T object);
|
|
|
|
boolean maybeHas(Predicate<T> predicate);
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
index b46c58c952e183bd74854c3eb70d64979af70f18..533167eaa8bd39006fb1c7e193c81359973da9af 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
|
|
@@ -71,6 +71,33 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
|
);
|
|
}
|
|
|
|
+ // Paper start - optimise palette reads
|
|
+ private void updateData(final PalettedContainer.Data<T> data) {
|
|
+ if (data != null) {
|
|
+ ((ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T>)(Object)data).moonrise$setPalette(
|
|
+ ((ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<T>)data.palette).moonrise$getRawPalette((ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T>)(Object)data)
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private T readPaletteSlow(final PalettedContainer.Data<T> data, final int paletteIdx) {
|
|
+ return data.palette.valueFor(paletteIdx);
|
|
+ }
|
|
+
|
|
+ private T readPalette(final PalettedContainer.Data<T> data, final int paletteIdx) {
|
|
+ final T[] palette = ((ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T>)(Object)data).moonrise$getPalette();
|
|
+ if (palette == null) {
|
|
+ return this.readPaletteSlow(data, paletteIdx);
|
|
+ }
|
|
+
|
|
+ final T ret = palette[paletteIdx];
|
|
+ if (ret == null) {
|
|
+ throw new IllegalArgumentException("Palette index out of bounds");
|
|
+ }
|
|
+ return ret;
|
|
+ }
|
|
+ // Paper end - optimise palette reads
|
|
+
|
|
public PalettedContainer(
|
|
IdMap<T> idList,
|
|
PalettedContainer.Strategy paletteProvider,
|
|
@@ -81,12 +108,14 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
|
this.registry = idList;
|
|
this.strategy = paletteProvider;
|
|
this.data = new PalettedContainer.Data<>(dataProvider, storage, dataProvider.factory().create(dataProvider.bits(), idList, this, paletteEntries));
|
|
+ this.updateData(this.data); // Paper - optimise palette reads
|
|
}
|
|
|
|
private PalettedContainer(IdMap<T> idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Data<T> data) {
|
|
this.registry = idList;
|
|
this.strategy = paletteProvider;
|
|
this.data = data;
|
|
+ this.updateData(this.data); // Paper - optimise palette reads
|
|
}
|
|
|
|
private PalettedContainer(PalettedContainer<T> container) {
|
|
@@ -100,6 +129,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
|
this.registry = idList;
|
|
this.data = this.createOrReuseData(null, 0);
|
|
this.data.palette.idFor(object);
|
|
+ this.updateData(this.data); // Paper - optimise palette reads
|
|
}
|
|
|
|
private PalettedContainer.Data<T> createOrReuseData(@Nullable PalettedContainer.Data<T> previousData, int bits) {
|
|
@@ -115,6 +145,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
|
PalettedContainer.Data<T> data2 = this.createOrReuseData(data, newBits);
|
|
data2.copyFrom(data.palette, data.storage);
|
|
this.data = data2;
|
|
+ this.updateData(this.data); // Paper - optimise palette reads
|
|
return data2.palette.idFor(object);
|
|
}
|
|
|
|
@@ -136,9 +167,12 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
|
}
|
|
|
|
private synchronized T getAndSet(int index, T value) { // Paper - synchronize
|
|
- int i = this.data.palette.idFor(value);
|
|
- int j = this.data.storage.getAndSet(index, i);
|
|
- return this.data.palette.valueFor(j);
|
|
+ // Paper start - optimise palette reads
|
|
+ final int paletteIdx = this.data.palette.idFor(value);
|
|
+ final PalettedContainer.Data<T> data = this.data;
|
|
+ final int prev = data.storage.getAndSet(index, paletteIdx);
|
|
+ return this.readPalette(data, prev);
|
|
+ // Paper end - optimise palette reads
|
|
}
|
|
|
|
public void set(int x, int y, int z, T value) {
|
|
@@ -162,8 +196,10 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
|
}
|
|
|
|
public T get(int index) { // Paper - public
|
|
- PalettedContainer.Data<T> data = this.data;
|
|
- return data.palette.valueFor(data.storage.get(index));
|
|
+ // Paper start - optimise palette reads
|
|
+ final PalettedContainer.Data<T> data = this.data;
|
|
+ return this.readPalette(data, data.storage.get(index));
|
|
+ // Paper end - optimise palette reads
|
|
}
|
|
|
|
@Override
|
|
@@ -183,6 +219,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
|
data.palette.read(buf);
|
|
buf.readLongArray(data.storage.getRaw());
|
|
this.data = data;
|
|
+ this.updateData(this.data); // Paper - optimise palette reads
|
|
} finally {
|
|
this.release();
|
|
}
|
|
@@ -323,7 +360,44 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
|
|
void accept(T object, int count);
|
|
}
|
|
|
|
- static record Data<T>(PalettedContainer.Configuration<T> configuration, BitStorage storage, Palette<T> palette) {
|
|
+ // Paper start - optimise palette reads
|
|
+ public static final class Data<T> implements ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T> {
|
|
+
|
|
+ private final PalettedContainer.Configuration<T> configuration;
|
|
+ private final BitStorage storage;
|
|
+ private final Palette<T> palette;
|
|
+
|
|
+ private T[] moonrise$palette;
|
|
+
|
|
+ public Data(final PalettedContainer.Configuration<T> configuration, final BitStorage storage, final Palette<T> palette) {
|
|
+ this.configuration = configuration;
|
|
+ this.storage = storage;
|
|
+ this.palette = palette;
|
|
+ }
|
|
+
|
|
+ public PalettedContainer.Configuration<T> configuration() {
|
|
+ return this.configuration;
|
|
+ }
|
|
+
|
|
+ public BitStorage storage() {
|
|
+ return this.storage;
|
|
+ }
|
|
+
|
|
+ public Palette<T> palette() {
|
|
+ return this.palette;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final T[] moonrise$getPalette() {
|
|
+ return this.moonrise$palette;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final void moonrise$setPalette(final T[] palette) {
|
|
+ this.moonrise$palette = palette;
|
|
+ }
|
|
+ // Paper end - optimise palette reads
|
|
+
|
|
public void copyFrom(Palette<T> palette, BitStorage storage) {
|
|
for (int i = 0; i < storage.getSize(); i++) {
|
|
T object = palette.valueFor(storage.get(i));
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/SingleValuePalette.java b/src/main/java/net/minecraft/world/level/chunk/SingleValuePalette.java
|
|
index a45e6410600afc5464e5d29932c193786ce0a6fb..a1ba68c95c2cdebdc0d7782cce7895529918073c 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/SingleValuePalette.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/SingleValuePalette.java
|
|
@@ -8,12 +8,24 @@ import net.minecraft.network.FriendlyByteBuf;
|
|
import net.minecraft.network.VarInt;
|
|
import org.apache.commons.lang3.Validate;
|
|
|
|
-public class SingleValuePalette<T> implements Palette<T> {
|
|
+public class SingleValuePalette<T> implements Palette<T>, ca.spottedleaf.moonrise.patches.fast_palette.FastPalette<T> { // Paper - optimise palette reads
|
|
private final IdMap<T> registry;
|
|
@Nullable
|
|
private T value;
|
|
private final PaletteResize<T> resizeHandler;
|
|
|
|
+ // Paper start - optimise palette reads
|
|
+ private T[] rawPalette;
|
|
+
|
|
+ @Override
|
|
+ public final T[] moonrise$getRawPalette(final ca.spottedleaf.moonrise.patches.fast_palette.FastPaletteData<T> container) {
|
|
+ if (this.rawPalette != null) {
|
|
+ return this.rawPalette;
|
|
+ }
|
|
+ return this.rawPalette = (T[])new Object[] { this.value };
|
|
+ }
|
|
+ // Paper end - optimise palette reads
|
|
+
|
|
public SingleValuePalette(IdMap<T> idList, PaletteResize<T> listener, List<T> entries) {
|
|
this.registry = idList;
|
|
this.resizeHandler = listener;
|
|
@@ -33,6 +45,11 @@ public class SingleValuePalette<T> implements Palette<T> {
|
|
return this.resizeHandler.onResize(1, object);
|
|
} else {
|
|
this.value = object;
|
|
+ // Paper start - optimise palette reads
|
|
+ if (this.rawPalette != null) {
|
|
+ this.rawPalette[0] = object;
|
|
+ }
|
|
+ // Paper end - optimise palette reads
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -58,6 +75,11 @@ public class SingleValuePalette<T> implements Palette<T> {
|
|
@Override
|
|
public void read(FriendlyByteBuf buf) {
|
|
this.value = this.registry.byIdOrThrow(buf.readVarInt());
|
|
+ // Paper start - optimise palette reads
|
|
+ if (this.rawPalette != null) {
|
|
+ this.rawPalette[0] = this.value;
|
|
+ }
|
|
+ // Paper end - optimise palette reads
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
|
index f1237f6fd6414900ffbad0caee31aa83310eeef4..8071ce70d66909bb4bda45792bf329a939d6f918 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
|
|
@@ -25,7 +25,7 @@ import net.minecraft.util.profiling.jfr.JvmProfiler;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import org.slf4j.Logger;
|
|
|
|
-public class RegionFile implements AutoCloseable {
|
|
+public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile { // Paper - rewrite chunk system
|
|
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final int SECTOR_BYTES = 4096;
|
|
@@ -49,6 +49,21 @@ public class RegionFile implements AutoCloseable {
|
|
@VisibleForTesting
|
|
protected final RegionBitmap usedSectors;
|
|
|
|
+ // Paper start - rewrite chunk system
|
|
+ @Override
|
|
+ public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite(final net.minecraft.nbt.CompoundTag data, final ChunkPos pos) throws IOException {
|
|
+ final RegionFile.ChunkBuffer buffer = ((RegionFile)(Object)this).new ChunkBuffer(pos);
|
|
+ ((ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkBuffer)buffer).moonrise$setWriteOnClose(false);
|
|
+
|
|
+ final DataOutputStream out = new DataOutputStream(this.version.wrap(buffer));
|
|
+
|
|
+ return new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData(
|
|
+ data, ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData.WriteResult.WRITE,
|
|
+ out, ((ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkBuffer)buffer)::moonrise$write
|
|
+ );
|
|
+ }
|
|
+ // Paper end - rewrite chunk system
|
|
+
|
|
public RegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync) throws IOException {
|
|
this(storageKey, directory, path, RegionFileVersion.getSelected(), dsync);
|
|
}
|
|
@@ -220,6 +235,16 @@ public class RegionFile implements AutoCloseable {
|
|
|
|
@Nullable
|
|
private DataInputStream createExternalChunkInputStream(ChunkPos pos, byte flags) throws IOException {
|
|
+ // Paper start - rewrite chunk system
|
|
+ final DataInputStream is = this.createExternalChunkInputStream0(pos, flags);
|
|
+ if (is == null) {
|
|
+ return is;
|
|
+ }
|
|
+ return new ca.spottedleaf.moonrise.patches.chunk_system.util.stream.ExternalChunkStreamMarker(is);
|
|
+ }
|
|
+ @Nullable
|
|
+ private DataInputStream createExternalChunkInputStream0(ChunkPos pos, byte flags) throws IOException {
|
|
+ // Paper end - rewrite chunk system
|
|
Path path = this.getExternalChunkPath(pos);
|
|
|
|
if (!Files.isRegularFile(path, new LinkOption[0])) {
|
|
@@ -443,10 +468,29 @@ public class RegionFile implements AutoCloseable {
|
|
}
|
|
|
|
public static final int MAX_CHUNK_SIZE = 500 * 1024 * 1024; // Paper - don't write garbage data to disk if writing serialization fails
|
|
- private class ChunkBuffer extends ByteArrayOutputStream {
|
|
+ private class ChunkBuffer extends ByteArrayOutputStream implements ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkBuffer { // Paper - rewrite chunk system
|
|
|
|
private final ChunkPos pos;
|
|
|
|
+ // Paper start - rewrite chunk system
|
|
+ private boolean writeOnClose = true;
|
|
+
|
|
+ @Override
|
|
+ public final boolean moonrise$getWriteOnClose() {
|
|
+ return this.writeOnClose;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final void moonrise$setWriteOnClose(final boolean value) {
|
|
+ this.writeOnClose = value;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final void moonrise$write(final RegionFile regionFile) throws IOException {
|
|
+ regionFile.write(this.pos, ByteBuffer.wrap(this.buf, 0, this.count));
|
|
+ }
|
|
+ // Paper end - rewrite chunk system
|
|
+
|
|
public ChunkBuffer(final ChunkPos chunkcoordintpair) {
|
|
super(8096);
|
|
super.write(0);
|
|
@@ -480,7 +524,7 @@ public class RegionFile implements AutoCloseable {
|
|
|
|
JvmProfiler.INSTANCE.onRegionFileWrite(RegionFile.this.info, this.pos, RegionFile.this.version, i);
|
|
bytebuffer.putInt(0, i);
|
|
- RegionFile.this.write(this.pos, bytebuffer);
|
|
+ if (this.writeOnClose) { RegionFile.this.write(this.pos, bytebuffer); } // Paper - rewrite chunk system
|
|
}
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
|
index 18054304e08c8a6346c0135a0e6a68e77fe5c37c..9dbc9e2f9d5aab71720bb81803efe76e2f361f04 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
|
|
@@ -28,8 +28,8 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
|
|
|
// Paper start - rewrite chunk system
|
|
private static final int REGION_SHIFT = 5;
|
|
- private static final int MAX_NON_EXISTING_CACHE = 1024 * 64;
|
|
- private final it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet nonExistingRegionFiles = new it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet(MAX_NON_EXISTING_CACHE+1);
|
|
+ private static final int MAX_NON_EXISTING_CACHE = 1024 * 4;
|
|
+ private final it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet nonExistingRegionFiles = new it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet();
|
|
private static String getRegionFileName(final int chunkX, final int chunkZ) {
|
|
return "r." + (chunkX >> REGION_SHIFT) + "." + (chunkZ >> REGION_SHIFT) + ".mca";
|
|
}
|
|
@@ -104,6 +104,97 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
|
|
|
return ret;
|
|
}
|
|
+
|
|
+ @Override
|
|
+ public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite(
|
|
+ final int chunkX, final int chunkZ, final CompoundTag compound
|
|
+ ) throws IOException {
|
|
+ if (compound == null) {
|
|
+ return new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData(
|
|
+ compound, ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData.WriteResult.DELETE,
|
|
+ null, null
|
|
+ );
|
|
+ }
|
|
+
|
|
+ final ChunkPos pos = new ChunkPos(chunkX, chunkZ);
|
|
+ final RegionFile regionFile = this.getRegionFile(pos);
|
|
+
|
|
+ // note: not required to keep regionfile loaded after this call, as the write param takes a regionfile as input
|
|
+ // (and, the regionfile parameter is unused for writing until the write call)
|
|
+ final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData writeData = ((ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile)regionFile).moonrise$startWrite(compound, pos);
|
|
+
|
|
+ try {
|
|
+ NbtIo.write(compound, writeData.output());
|
|
+ } finally {
|
|
+ writeData.output().close();
|
|
+ }
|
|
+
|
|
+ return writeData;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final void moonrise$finishWrite(
|
|
+ final int chunkX, final int chunkZ, final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData writeData
|
|
+ ) throws IOException {
|
|
+ final ChunkPos pos = new ChunkPos(chunkX, chunkZ);
|
|
+ if (writeData.result() == ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData.WriteResult.DELETE) {
|
|
+ final RegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ);
|
|
+ if (regionFile != null) {
|
|
+ regionFile.clear(pos);
|
|
+ } // else: didn't exist
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ writeData.write().run(this.getRegionFile(pos));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData moonrise$readData(
|
|
+ final int chunkX, final int chunkZ
|
|
+ ) throws IOException {
|
|
+ final RegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ);
|
|
+
|
|
+ final DataInputStream input = regionFile == null ? null : regionFile.getChunkDataInputStream(new ChunkPos(chunkX, chunkZ));
|
|
+
|
|
+ if (input == null) {
|
|
+ return new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData(
|
|
+ ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData.ReadResult.NO_DATA, null, null
|
|
+ );
|
|
+ }
|
|
+
|
|
+ final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData ret = new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData(
|
|
+ ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData.ReadResult.HAS_DATA, input, null
|
|
+ );
|
|
+
|
|
+ if (!(input instanceof ca.spottedleaf.moonrise.patches.chunk_system.util.stream.ExternalChunkStreamMarker)) {
|
|
+ // internal stream, which is fully read
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ final CompoundTag syncRead = this.moonrise$finishRead(chunkX, chunkZ, ret);
|
|
+
|
|
+ if (syncRead == null) {
|
|
+ // need to try again
|
|
+ return this.moonrise$readData(chunkX, chunkZ);
|
|
+ }
|
|
+
|
|
+ return new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData(
|
|
+ ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData.ReadResult.SYNC_READ, null, syncRead
|
|
+ );
|
|
+ }
|
|
+
|
|
+ // if the return value is null, then the caller needs to re-try with a new call to readData()
|
|
+ @Override
|
|
+ public final CompoundTag moonrise$finishRead(
|
|
+ final int chunkX, final int chunkZ, final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData readData
|
|
+ ) throws IOException {
|
|
+ try {
|
|
+ return NbtIo.read(readData.input());
|
|
+ } finally {
|
|
+ readData.input().close();
|
|
+ }
|
|
+ }
|
|
// Paper end - rewrite chunk system
|
|
|
|
protected RegionFileStorage(RegionStorageInfo storageKey, Path directory, boolean dsync) { // Paper - protected
|
|
@@ -112,6 +203,12 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
|
|
this.info = storageKey;
|
|
}
|
|
|
|
+ // Paper start - rewrite chunk system
|
|
+ public RegionFile getRegionFile(ChunkPos chunkcoordintpair) throws IOException {
|
|
+ return this.getRegionFile(chunkcoordintpair, false);
|
|
+ }
|
|
+ // Paper end - rewrite chunk system
|
|
+
|
|
public RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - public
|
|
// Paper start - rewrite chunk system
|
|
if (existingOnly) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
index 261e5994d13f8bc30490b86691c80c0a21e7640a..f4fbcbb8ff6d2677af1a02a0801a323c06dce9b1 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
|
|
@@ -55,6 +55,48 @@ public abstract class FlowingFluid extends Fluid {
|
|
});
|
|
private final Map<FluidState, VoxelShape> shapes = Maps.newIdentityHashMap();
|
|
|
|
+ // Paper start - fluid method optimisations
|
|
+ private FluidState sourceFalling;
|
|
+ private FluidState sourceNotFalling;
|
|
+
|
|
+ private static final int TOTAL_FLOWING_STATES = FALLING.getPossibleValues().size() * LEVEL.getPossibleValues().size();
|
|
+ private static final int MIN_LEVEL = LEVEL.getPossibleValues().stream().sorted().findFirst().get().intValue();
|
|
+
|
|
+ // index = (falling ? 1 : 0) + level*2
|
|
+ private FluidState[] flowingLookUp;
|
|
+ private volatile boolean init;
|
|
+
|
|
+ private static final int COLLISION_OCCLUSION_CACHE_SIZE = 2048;
|
|
+ private static final ThreadLocal<ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey[]> COLLISION_OCCLUSION_CACHE = ThreadLocal.withInitial(() -> new ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey[COLLISION_OCCLUSION_CACHE_SIZE]);
|
|
+
|
|
+
|
|
+ /**
|
|
+ * Due to init order, we need to use callbacks to initialise our state
|
|
+ */
|
|
+ private void init() {
|
|
+ synchronized (this) {
|
|
+ if (this.init) {
|
|
+ return;
|
|
+ }
|
|
+ this.flowingLookUp = new FluidState[TOTAL_FLOWING_STATES];
|
|
+ final FluidState defaultFlowState = this.getFlowing().defaultFluidState();
|
|
+ for (int i = 0; i < TOTAL_FLOWING_STATES; ++i) {
|
|
+ final int falling = i & 1;
|
|
+ final int level = (i >>> 1) + MIN_LEVEL;
|
|
+
|
|
+ this.flowingLookUp[i] = defaultFlowState.setValue(FALLING, falling == 1 ? Boolean.TRUE : Boolean.FALSE)
|
|
+ .setValue(LEVEL, Integer.valueOf(level));
|
|
+ }
|
|
+
|
|
+ final FluidState defaultFallState = this.getSource().defaultFluidState();
|
|
+ this.sourceFalling = defaultFallState.setValue(FALLING, Boolean.TRUE);
|
|
+ this.sourceNotFalling = defaultFallState.setValue(FALLING, Boolean.FALSE);
|
|
+
|
|
+ this.init = true;
|
|
+ }
|
|
+ }
|
|
+ // Paper end - fluid method optimisations
|
|
+
|
|
public FlowingFluid() {}
|
|
|
|
@Override
|
|
@@ -246,65 +288,70 @@ public abstract class FlowingFluid extends Fluid {
|
|
}
|
|
}
|
|
|
|
- private static boolean canPassThroughWall(Direction face, BlockGetter world, BlockPos pos, BlockState state, BlockPos fromPos, BlockState fromState) {
|
|
- VoxelShape voxelshape = fromState.getCollisionShape(world, fromPos);
|
|
+ // Paper start - fluid method optimisations
|
|
+ private static boolean canPassThroughWall(final Direction direction, final BlockGetter level,
|
|
+ final BlockPos fromPos, final BlockState fromState,
|
|
+ final BlockPos toPos, final BlockState toState) {
|
|
+ if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)fromState).moonrise$emptyCollisionShape() & ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)toState).moonrise$emptyCollisionShape()) {
|
|
+ // don't even try to cache simple cases
|
|
+ return true;
|
|
+ }
|
|
|
|
- if (voxelshape == Shapes.block()) {
|
|
+ if (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)fromState).moonrise$occludesFullBlock() | ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)toState).moonrise$occludesFullBlock()) {
|
|
+ // don't even try to cache simple cases
|
|
return false;
|
|
- } else {
|
|
- VoxelShape voxelshape1 = state.getCollisionShape(world, pos);
|
|
-
|
|
- if (voxelshape1 == Shapes.block()) {
|
|
- return false;
|
|
- } else if (voxelshape1 == Shapes.empty() && voxelshape == Shapes.empty()) {
|
|
- return true;
|
|
- } else {
|
|
- Object2ByteLinkedOpenHashMap object2bytelinkedopenhashmap;
|
|
-
|
|
- if (!state.getBlock().hasDynamicShape() && !fromState.getBlock().hasDynamicShape()) {
|
|
- object2bytelinkedopenhashmap = (Object2ByteLinkedOpenHashMap) FlowingFluid.OCCLUSION_CACHE.get();
|
|
- } else {
|
|
- object2bytelinkedopenhashmap = null;
|
|
- }
|
|
+ }
|
|
|
|
- FlowingFluid.BlockStatePairKey fluidtypeflowing_a;
|
|
+ final ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey[] cache = ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)fromState).moonrise$hasCache() & ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)toState).moonrise$hasCache() ?
|
|
+ COLLISION_OCCLUSION_CACHE.get() : null;
|
|
|
|
- if (object2bytelinkedopenhashmap != null) {
|
|
- fluidtypeflowing_a = new FlowingFluid.BlockStatePairKey(state, fromState, face);
|
|
- byte b0 = object2bytelinkedopenhashmap.getAndMoveToFirst(fluidtypeflowing_a);
|
|
+ final int keyIndex
|
|
+ = (((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)fromState).moonrise$uniqueId1() ^ ((ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState)toState).moonrise$uniqueId2() ^ ((ca.spottedleaf.moonrise.patches.collisions.util.CollisionDirection)(Object)direction).moonrise$uniqueId())
|
|
+ & (COLLISION_OCCLUSION_CACHE_SIZE - 1);
|
|
|
|
- if (b0 != 127) {
|
|
- return b0 != 0;
|
|
- }
|
|
- } else {
|
|
- fluidtypeflowing_a = null;
|
|
- }
|
|
-
|
|
- boolean flag = !Shapes.mergedFaceOccludes(voxelshape1, voxelshape, face);
|
|
+ if (cache != null) {
|
|
+ final ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey cached = cache[keyIndex];
|
|
+ if (cached != null && cached.first() == fromState && cached.second() == toState && cached.direction() == direction) {
|
|
+ return cached.result();
|
|
+ }
|
|
+ }
|
|
|
|
- if (object2bytelinkedopenhashmap != null) {
|
|
- if (object2bytelinkedopenhashmap.size() == 200) {
|
|
- object2bytelinkedopenhashmap.removeLastByte();
|
|
- }
|
|
+ final VoxelShape shape1 = fromState.getCollisionShape(level, fromPos);
|
|
+ final VoxelShape shape2 = toState.getCollisionShape(level, toPos);
|
|
|
|
- object2bytelinkedopenhashmap.putAndMoveToFirst(fluidtypeflowing_a, (byte) (flag ? 1 : 0));
|
|
- }
|
|
+ final boolean result = !Shapes.mergedFaceOccludes(shape1, shape2, direction);
|
|
|
|
- return flag;
|
|
- }
|
|
+ if (cache != null) {
|
|
+ // we can afford to replace in-use keys more often due to the excessive caching the collision patch does in mergedFaceOccludes
|
|
+ cache[keyIndex] = new ca.spottedleaf.moonrise.patches.collisions.util.FluidOcclusionCacheKey(fromState, toState, direction, result);
|
|
}
|
|
+
|
|
+ return result;
|
|
}
|
|
+ // Paper end - fluid method optimisations
|
|
|
|
public abstract Fluid getFlowing();
|
|
|
|
public FluidState getFlowing(int level, boolean falling) {
|
|
- return (FluidState) ((FluidState) this.getFlowing().defaultFluidState().setValue(FlowingFluid.LEVEL, level)).setValue(FlowingFluid.FALLING, falling);
|
|
+ // Paper start - fluid method optimisations
|
|
+ final int amount = level;
|
|
+ if (!this.init) {
|
|
+ this.init();
|
|
+ }
|
|
+ final int index = (falling ? 1 : 0) | ((amount - MIN_LEVEL) << 1);
|
|
+ return this.flowingLookUp[index];
|
|
+ // Paper end - fluid method optimisations
|
|
}
|
|
|
|
public abstract Fluid getSource();
|
|
|
|
public FluidState getSource(boolean falling) {
|
|
- return (FluidState) this.getSource().defaultFluidState().setValue(FlowingFluid.FALLING, falling);
|
|
+ // Paper start - fluid method optimisations
|
|
+ if (!this.init) {
|
|
+ this.init();
|
|
+ }
|
|
+ return falling ? this.sourceFalling : this.sourceNotFalling;
|
|
+ // Paper end - fluid method optimisations
|
|
}
|
|
|
|
protected abstract boolean canConvertToSource(ServerLevel world);
|
|
diff --git a/src/main/java/net/minecraft/world/level/material/FluidState.java b/src/main/java/net/minecraft/world/level/material/FluidState.java
|
|
index 87adfe152abd1b8b4d547034576883c5d1cdf134..2d50d72bf026d0cf9c546a3c6fc1859379bfd805 100644
|
|
--- a/src/main/java/net/minecraft/world/level/material/FluidState.java
|
|
+++ b/src/main/java/net/minecraft/world/level/material/FluidState.java
|
|
@@ -22,12 +22,30 @@ import net.minecraft.world.level.block.state.properties.Property;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
|
|
-public final class FluidState extends StateHolder<Fluid, FluidState> {
|
|
+public final class FluidState extends StateHolder<Fluid, FluidState> implements ca.spottedleaf.moonrise.patches.fluid.FluidFluidState { // Paper - fluid method optimisations
|
|
public static final Codec<FluidState> CODEC = codec(BuiltInRegistries.FLUID.byNameCodec(), Fluid::defaultFluidState).stable();
|
|
public static final int AMOUNT_MAX = 9;
|
|
public static final int AMOUNT_FULL = 8;
|
|
protected final boolean isEmpty; // Paper - Perf: moved from isEmpty()
|
|
|
|
+ // Paper start - fluid method optimisations
|
|
+ private int amount;
|
|
+ //private boolean isEmpty;
|
|
+ private boolean isSource;
|
|
+ private float ownHeight;
|
|
+ private boolean isRandomlyTicking;
|
|
+ private BlockState legacyBlock;
|
|
+
|
|
+ @Override
|
|
+ public final void moonrise$initCaches() {
|
|
+ this.amount = this.getType().getAmount((FluidState)(Object)this);
|
|
+ //this.isEmpty = this.getType().isEmpty();
|
|
+ this.isSource = this.getType().isSource((FluidState)(Object)this);
|
|
+ this.ownHeight = this.getType().getOwnHeight((FluidState)(Object)this);
|
|
+ this.isRandomlyTicking = this.getType().isRandomlyTicking();
|
|
+ }
|
|
+ // Paper end - fluid method optimisations
|
|
+
|
|
public FluidState(Fluid fluid, Reference2ObjectArrayMap<Property<?>, Comparable<?>> propertyMap, MapCodec<FluidState> codec) {
|
|
super(fluid, propertyMap, codec);
|
|
this.isEmpty = fluid.isEmpty(); // Paper - Perf: moved from isEmpty()
|
|
@@ -38,11 +56,11 @@ public final class FluidState extends StateHolder<Fluid, FluidState> {
|
|
}
|
|
|
|
public boolean isSource() {
|
|
- return this.getType().isSource(this);
|
|
+ return this.isSource; // Paper - fluid method optimisations
|
|
}
|
|
|
|
public boolean isSourceOfType(Fluid fluid) {
|
|
- return this.owner == fluid && this.owner.isSource(this);
|
|
+ return this.isSource && this.owner == fluid; // Paper - fluid method optimisations
|
|
}
|
|
|
|
public boolean isEmpty() {
|
|
@@ -54,11 +72,11 @@ public final class FluidState extends StateHolder<Fluid, FluidState> {
|
|
}
|
|
|
|
public float getOwnHeight() {
|
|
- return this.getType().getOwnHeight(this);
|
|
+ return this.ownHeight; // Paper - fluid method optimisations
|
|
}
|
|
|
|
public int getAmount() {
|
|
- return this.getType().getAmount(this);
|
|
+ return this.amount; // Paper - fluid method optimisations
|
|
}
|
|
|
|
public boolean shouldRenderBackwardUpFace(BlockGetter world, BlockPos pos) {
|
|
@@ -84,7 +102,7 @@ public final class FluidState extends StateHolder<Fluid, FluidState> {
|
|
}
|
|
|
|
public boolean isRandomlyTicking() {
|
|
- return this.getType().isRandomlyTicking();
|
|
+ return this.isRandomlyTicking; // Paper - fluid method optimisations
|
|
}
|
|
|
|
public void randomTick(ServerLevel world, BlockPos pos, RandomSource random) {
|
|
@@ -96,7 +114,12 @@ public final class FluidState extends StateHolder<Fluid, FluidState> {
|
|
}
|
|
|
|
public BlockState createLegacyBlock() {
|
|
- return this.getType().createLegacyBlock(this);
|
|
+ // Paper start - fluid method optimisations
|
|
+ if (this.legacyBlock != null) {
|
|
+ return this.legacyBlock;
|
|
+ }
|
|
+ return this.legacyBlock = this.getType().createLegacyBlock((FluidState)(Object)this);
|
|
+ // Paper end - fluid method optimisations
|
|
}
|
|
|
|
@Nullable
|
|
diff --git a/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java
|
|
index 1d36f8dcffd22cf844448d3d8351fb8718cf5227..fbe0c4b0fdbb992b7002f6afe1e74d63cbb420f2 100644
|
|
--- a/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java
|
|
+++ b/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java
|
|
@@ -57,7 +57,7 @@ public abstract class DiscreteVoxelShape implements ca.spottedleaf.moonrise.patc
|
|
}
|
|
}
|
|
|
|
- final boolean hasSingleAABB = sizeX == 1 && sizeY == 1 && sizeZ == 1 && !isEmpty && discreteVoxelShape.isFull(0, 0, 0);
|
|
+ final boolean hasSingleAABB = sizeX == 1 && sizeY == 1 && sizeZ == 1 && !isEmpty && (voxelSet[0] & 1L) != 0L;
|
|
|
|
final int minFullX = discreteVoxelShape.firstFull(Direction.Axis.X);
|
|
final int minFullY = discreteVoxelShape.firstFull(Direction.Axis.Y);
|
|
diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
|
|
index 672a2038c6d8b31090403766460c6149a75adf8b..513bed7f11aee667c87046db4cf912b80e8f3638 100644
|
|
--- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
|
|
+++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
|
|
@@ -180,13 +180,13 @@ public final class Shapes {
|
|
final VoxelShape first = tmp[i];
|
|
final VoxelShape second = tmp[next];
|
|
|
|
- tmp[newSize++] = Shapes.or(first, second);
|
|
+ tmp[newSize++] = Shapes.joinUnoptimized(first, second, BooleanOp.OR);
|
|
}
|
|
}
|
|
size = newSize;
|
|
}
|
|
|
|
- return tmp[0];
|
|
+ return tmp[0].optimize();
|
|
// Paper end - optimise collisions
|
|
}
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java
|
|
index d850a7de74150a04622da71d9614320f3d5d69e8..3f8e7e29c3e52211a29e6f0a32890f6b53bfd9a8 100644
|
|
--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java
|
|
+++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java
|
|
@@ -162,13 +162,13 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
|
|
if (direction.getAxisDirection() == Direction.AxisDirection.POSITIVE) {
|
|
if (DoubleMath.fuzzyEquals(this.max(axis), 1.0, ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) {
|
|
- ret = tryForceBlock(new SliceShape((VoxelShape)(Object)this, axis, this.shape.getSize(axis) - 1));
|
|
+ ret = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.sliceShape((VoxelShape)(Object)this, axis, this.shape.getSize(axis) - 1);
|
|
} else {
|
|
ret = Shapes.empty();
|
|
}
|
|
} else {
|
|
if (DoubleMath.fuzzyEquals(this.min(axis), 0.0, ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) {
|
|
- ret = tryForceBlock(new SliceShape((VoxelShape)(Object)this, axis, 0));
|
|
+ ret = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.sliceShape((VoxelShape)(Object)this, axis, 0);
|
|
} else {
|
|
ret = Shapes.empty();
|
|
}
|
|
@@ -179,23 +179,6 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
return ret;
|
|
}
|
|
|
|
- private static VoxelShape tryForceBlock(final VoxelShape other) {
|
|
- if (other == Shapes.block()) {
|
|
- return other;
|
|
- }
|
|
-
|
|
- final AABB otherAABB = ((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)other).moonrise$getSingleAABBRepresentation();
|
|
- if (otherAABB == null) {
|
|
- return other;
|
|
- }
|
|
-
|
|
- if (((ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape)Shapes.block()).moonrise$getSingleAABBRepresentation().equals(otherAABB)) {
|
|
- return Shapes.block();
|
|
- }
|
|
-
|
|
- return other;
|
|
- }
|
|
-
|
|
private boolean computeOccludesFullBlock() {
|
|
if (this.isEmpty) {
|
|
this.occludesFullBlock = Boolean.FALSE;
|
|
@@ -293,18 +276,21 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
return result;
|
|
}
|
|
|
|
- private static DoubleList offsetList(final DoubleList src, final double by) {
|
|
- if (src instanceof OffsetDoubleList offsetDoubleList) {
|
|
- return new OffsetDoubleList(offsetDoubleList.delegate, by + offsetDoubleList.offset);
|
|
+ private static DoubleList offsetList(final double[] src, final double by) {
|
|
+ final it.unimi.dsi.fastutil.doubles.DoubleArrayList wrap = it.unimi.dsi.fastutil.doubles.DoubleArrayList.wrap(src);
|
|
+ if (by == 0.0) {
|
|
+ return wrap;
|
|
}
|
|
- return new OffsetDoubleList(src, by);
|
|
+ return new OffsetDoubleList(wrap, by);
|
|
}
|
|
|
|
private List<AABB> toAabbsUncached() {
|
|
- final List<AABB> ret = new java.util.ArrayList<>();
|
|
+ final List<AABB> ret;
|
|
if (this.singleAABBRepresentation != null) {
|
|
+ ret = new java.util.ArrayList<>(1);
|
|
ret.add(this.singleAABBRepresentation);
|
|
} else {
|
|
+ ret = new java.util.ArrayList<>();
|
|
final double[] coordsX = this.rootCoordinatesX;
|
|
final double[] coordsY = this.rootCoordinatesY;
|
|
final double[] coordsZ = this.rootCoordinatesZ;
|
|
@@ -426,6 +412,26 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
final double minDistance = minDistanceArr[0];
|
|
return new BlockHitResult(from.add(minDistance * diffX, minDistance * diffY, minDistance * diffZ), direction, offset, false);
|
|
}
|
|
+
|
|
+ private VoxelShape calculateFaceDirect(final Direction direction, final Direction.Axis axis, final double[] coords, final double offset) {
|
|
+ if (coords.length == 2 &&
|
|
+ DoubleMath.fuzzyEquals(coords[0] + offset, 0.0, ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) &&
|
|
+ DoubleMath.fuzzyEquals(coords[1] + offset, 1.0, ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) {
|
|
+ return (VoxelShape)(Object)this;
|
|
+ }
|
|
+
|
|
+ final boolean positiveDir = direction.getAxisDirection() == Direction.AxisDirection.POSITIVE;
|
|
+
|
|
+ // see findIndex
|
|
+ final int index = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.findFloor(
|
|
+ coords, (positiveDir ? (1.0 - ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON) : (0.0 + ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_EPSILON)) - offset,
|
|
+ 0, coords.length - 1
|
|
+ );
|
|
+
|
|
+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.sliceShape(
|
|
+ (VoxelShape)(Object)this, axis, index
|
|
+ );
|
|
+ }
|
|
// Paper end - optimise collisions
|
|
|
|
protected VoxelShape(DiscreteVoxelShape voxels) {
|
|
@@ -517,20 +523,32 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
}
|
|
|
|
public VoxelShape singleEncompassing() {
|
|
- return this.isEmpty()
|
|
- ? Shapes.empty()
|
|
- : Shapes.box(
|
|
- this.min(Direction.Axis.X),
|
|
- this.min(Direction.Axis.Y),
|
|
- this.min(Direction.Axis.Z),
|
|
- this.max(Direction.Axis.X),
|
|
- this.max(Direction.Axis.Y),
|
|
- this.max(Direction.Axis.Z)
|
|
- );
|
|
+ // Paper start - optimise collisions
|
|
+ if (this.isEmpty) {
|
|
+ return Shapes.empty();
|
|
+ }
|
|
+ return Shapes.create(this.bounds());
|
|
+ // Paper end - optimise collisions
|
|
}
|
|
|
|
protected double get(Direction.Axis axis, int index) {
|
|
- return this.getCoords(axis).getDouble(index);
|
|
+ // Paper start - optimise collisions
|
|
+ final int idx = index;
|
|
+ switch (axis) {
|
|
+ case X: {
|
|
+ return this.rootCoordinatesX[idx] + this.offsetX;
|
|
+ }
|
|
+ case Y: {
|
|
+ return this.rootCoordinatesY[idx] + this.offsetY;
|
|
+ }
|
|
+ case Z: {
|
|
+ return this.rootCoordinatesZ[idx] + this.offsetZ;
|
|
+ }
|
|
+ default: {
|
|
+ throw new IllegalStateException("Unknown axis: " + axis);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - optimise collisions
|
|
}
|
|
|
|
public abstract DoubleList getCoords(Direction.Axis axis);
|
|
@@ -551,9 +569,9 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
|
|
final ArrayVoxelShape ret = new ArrayVoxelShape(
|
|
this.shape,
|
|
- offsetList(this.getCoords(Direction.Axis.X), x),
|
|
- offsetList(this.getCoords(Direction.Axis.Y), y),
|
|
- offsetList(this.getCoords(Direction.Axis.Z), z)
|
|
+ offsetList(this.rootCoordinatesX, this.offsetX + x),
|
|
+ offsetList(this.rootCoordinatesY, this.offsetY + y),
|
|
+ offsetList(this.rootCoordinatesZ, this.offsetZ + z)
|
|
);
|
|
|
|
final ca.spottedleaf.moonrise.patches.collisions.shape.CachedToAABBs cachedToAABBs = this.cachedToAABBs;
|
|
@@ -578,6 +596,11 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
|
|
final List<AABB> aabbs = this.toAabbs();
|
|
|
|
+ if (aabbs.isEmpty()) {
|
|
+ // We are a SliceShape, which does not properly fill isEmpty for every case
|
|
+ return Shapes.empty();
|
|
+ }
|
|
+
|
|
if (aabbs.size() == 1) {
|
|
final AABB singleAABB = aabbs.get(0);
|
|
final VoxelShape ret = Shapes.create(singleAABB);
|
|
@@ -704,7 +727,32 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
}
|
|
|
|
protected int findIndex(Direction.Axis axis, double coord) {
|
|
- return Mth.binarySearch(0, this.shape.getSize(axis) + 1, i -> coord < this.get(axis, i)) - 1;
|
|
+ // Paper start - optimise collisions
|
|
+ final double value = coord;
|
|
+ switch (axis) {
|
|
+ case X: {
|
|
+ final double[] values = this.rootCoordinatesX;
|
|
+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.findFloor(
|
|
+ values, value - this.offsetX, 0, values.length - 1
|
|
+ );
|
|
+ }
|
|
+ case Y: {
|
|
+ final double[] values = this.rootCoordinatesY;
|
|
+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.findFloor(
|
|
+ values, value - this.offsetY, 0, values.length - 1
|
|
+ );
|
|
+ }
|
|
+ case Z: {
|
|
+ final double[] values = this.rootCoordinatesZ;
|
|
+ return ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.findFloor(
|
|
+ values, value - this.offsetZ, 0, values.length - 1
|
|
+ );
|
|
+ }
|
|
+ default: {
|
|
+ throw new IllegalStateException("Unknown axis: " + axis);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - optimise collisions
|
|
}
|
|
|
|
@Nullable
|
|
@@ -727,13 +775,13 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
final AABB singleAABB = this.singleAABBRepresentation;
|
|
if (singleAABB != null) {
|
|
if (singleAABB.contains(fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) {
|
|
- return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
|
|
+ return new BlockHitResult(fromBehind, Direction.getApproximateNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
|
|
}
|
|
return clip(singleAABB, from, to, offset);
|
|
}
|
|
|
|
if (ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.strictlyContains((VoxelShape)(Object)this, fromBehindOffsetX, fromBehindOffsetY, fromBehindOffsetZ)) {
|
|
- return new BlockHitResult(fromBehind, Direction.getNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
|
|
+ return new BlockHitResult(fromBehind, Direction.getApproximateNearest(directionOpposite.x, directionOpposite.y, directionOpposite.z).getOpposite(), offset, true);
|
|
}
|
|
|
|
return AABB.clip(((VoxelShape)(Object)this).toAabbs(), from, to, offset);
|
|
@@ -786,20 +834,24 @@ public abstract class VoxelShape implements ca.spottedleaf.moonrise.patches.coll
|
|
}
|
|
}
|
|
|
|
- private VoxelShape calculateFace(Direction facing) {
|
|
- Direction.Axis axis = facing.getAxis();
|
|
- if (this.isCubeLikeAlong(axis)) {
|
|
- return this;
|
|
- } else {
|
|
- Direction.AxisDirection axisDirection = facing.getAxisDirection();
|
|
- int i = this.findIndex(axis, axisDirection == Direction.AxisDirection.POSITIVE ? 0.9999999 : 1.0E-7);
|
|
- SliceShape sliceShape = new SliceShape(this, axis, i);
|
|
- if (sliceShape.isEmpty()) {
|
|
- return Shapes.empty();
|
|
- } else {
|
|
- return (VoxelShape)(sliceShape.isCubeLike() ? Shapes.block() : sliceShape);
|
|
+ private VoxelShape calculateFace(Direction direction) {
|
|
+ // Paper start - optimise collisions
|
|
+ final Direction.Axis axis = direction.getAxis();
|
|
+ switch (axis) {
|
|
+ case X: {
|
|
+ return this.calculateFaceDirect(direction, axis, this.rootCoordinatesX, this.offsetX);
|
|
+ }
|
|
+ case Y: {
|
|
+ return this.calculateFaceDirect(direction, axis, this.rootCoordinatesY, this.offsetY);
|
|
+ }
|
|
+ case Z: {
|
|
+ return this.calculateFaceDirect(direction, axis, this.rootCoordinatesZ, this.offsetZ);
|
|
+ }
|
|
+ default: {
|
|
+ throw new IllegalStateException("Unknown axis: " + axis);
|
|
}
|
|
}
|
|
+ // Paper end - optimise collisions
|
|
}
|
|
|
|
protected boolean isCubeLike() {
|