Archiviert
13
0
Dieses Repository wurde am 2024-12-25 archiviert. Du kannst Dateien ansehen und es klonen, aber nicht pushen oder Issues/Pull-Requests öffnen.
Paper-Old/patches/server/0828-fixup-Moonrise-optimisation-patches.patch
Spottedleaf ecf4d9715e Begin fixing issues
See diff in the update text file
2024-10-25 11:25:09 -07:00

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() {