Files
paper-mc/patches/server/fixup-Moonrise-optimisation-patches.patch
Spottedleaf 77baea3bf9 Begin fixing issues
See diff in the update text file
2024-10-24 08:18:20 -07:00

13350 lines
685 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccess.java
@@ -0,0 +0,0 @@
+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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/PropertyAccessStateHolder.java
@@ -0,0 +0,0 @@
+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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/blockstate_propertyaccess/util/ZeroCollidingReferenceStateTable.java
@@ -0,0 +0,0 @@
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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 {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystemFeatures.java
+++ /dev/null
@@ -0,0 +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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/async_save/AsyncChunkSaveData.java
+++ /dev/null
@@ -0,0 +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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java
@@ -0,0 +0,0 @@
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java
+++ /dev/null
@@ -0,0 +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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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 {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
}
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/ChunkSystemChunkMap.java
@@ -0,0 +0,0 @@
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/level/chunk/ChunkData.java
@@ -0,0 +0,0 @@
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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 {
@@ -0,0 +0,0 @@ public final class ChunkEntitySlices {
private final EntityList entities = new EntityList();
public FullChunkStatus status;
+ public final ChunkData chunkData;
private boolean isTransient;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
}
}
}
@@ -0,0 +0,0 @@ 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<>();
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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<>();
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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");
@@ -0,0 +0,0 @@ public abstract class EntityLookup implements LevelEntityGetter<Entity> {
return false;
}
- if (!this.screenEntity(entity)) {
+ if (!this.screenEntity(entity, fromDisk, event)) {
return false;
}
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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
}
@@ -0,0 +0,0 @@ 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
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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
);
}
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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));
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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) + "'");
}
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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"
+ );
}
}
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ public final class ChunkHolderManager {
final ChunkLoadTask.EntityDataLoadTask entityLoad = current.getEntityDataLoadTask();
if (entityLoad != null) {
- entityLoad.raisePriority(PrioritisedExecutor.Priority.BLOCKING);
+ entityLoad.raisePriority(Priority.BLOCKING);
}
}
}
@@ -0,0 +0,0 @@ public final class ChunkHolderManager {
final ChunkLoadTask.PoiDataLoadTask poiLoad = current.getPoiDataLoadTask();
if (poiLoad != null) {
- poiLoad.raisePriority(PrioritisedExecutor.Priority.BLOCKING);
+ poiLoad.raisePriority(Priority.BLOCKING);
}
}
} finally {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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));
}
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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");
}
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ public final class ChunkTaskScheduler {
final int neighbourReadRadius = Math.max(
0,
- chunkPyramid.getStepTo(toStatus).getAccumulatedRadiusOf(ChunkStatus.EMPTY)
+ chunkStep.getAccumulatedRadiusOf(ChunkStatus.EMPTY)
);
boolean unGeneratedNeighbours = false;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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() &&
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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<>();
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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<>();
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ public final class NewChunkHolder {
return neighbour;
}
- return PrioritisedExecutor.Priority.max(us, neighbour);
+ return Priority.max(us, neighbour);
}
private void recalculateNeighbourRequestedPriority() {
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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() {
@@ -0,0 +0,0 @@ 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() {
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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<>();
@@ -0,0 +0,0 @@ public final class NewChunkHolder {
LOGGER.error("Failed to process chunk status callback", thr);
}
}
- }, PrioritisedExecutor.Priority.HIGHEST);
+ }, Priority.HIGHEST);
}
// note: must hold scheduling lock
@@ -0,0 +0,0 @@ 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");
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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()));
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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 {
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ public abstract class PriorityHolder {
return;
}
- this.scheduleTask(PrioritisedExecutor.Priority.getPriority(priority));
+ this.scheduleTask(Priority.getPriority(priority));
int failures = 0;
for (;;) {
@@ -0,0 +0,0 @@ public abstract class PriorityHolder {
return;
}
- this.setPriorityScheduled(PrioritisedExecutor.Priority.getPriority(priority));
+ this.setPriorityScheduled(Priority.getPriority(priority));
++failures;
for (int i = 0; i < failures; ++i) {
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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() {
@@ -0,0 +0,0 @@ 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];
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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() {
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ public class RadiusAwarePrioritisedExecutor {
scheduleTasks(toSchedule);
}
+ @Override
+ public PrioritisedExecutor getExecutor() {
+ return this.scheduler.executor;
+ }
+
@Override
public void run() {
final Runnable run = this.run;
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ public class RadiusAwarePrioritisedExecutor {
}
@Override
- public PrioritisedExecutor.Priority getPriority() {
+ public Priority getPriority() {
final PrioritisedExecutor.PrioritisedTask task;
synchronized (this.scheduler) {
if ((task = this.queuedTask) == null) {
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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 {
@@ -0,0 +0,0 @@ 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));
}
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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();
}
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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 {
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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
);
}
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java
@@ -0,0 +0,0 @@
+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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemRegionFile.java
@@ -0,0 +0,0 @@
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/util/stream/ExternalChunkStreamMarker.java
@@ -0,0 +0,0 @@
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
@@ -0,0 +0,0 @@
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;
}
@@ -0,0 +0,0 @@ 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));
}
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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];
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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,
@@ -0,0 +0,0 @@ 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];
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ public final class CollisionUtil {
return true;
} else if (isEmpty1 ^ isEmpty2) {
return false;
- }
+ } // else: isEmpty1 = isEmpty2 = false
if (cachedShapeData1.hasSingleAABB() != cachedShapeData2.hasSingleAABB()) {
return false;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 */
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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)) {
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/ExplosionBlockCache.java
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/ExplosionBlockCache.java
@@ -0,0 +0,0 @@
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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())
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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();
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/util/EmptyStreamForMoveCall.java
+++ /dev/null
@@ -0,0 +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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/CollisionLevel.java
+++ /dev/null
@@ -0,0 +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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/fast_palette/FastPalette.java
@@ -0,0 +0,0 @@
+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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/fast_palette/FastPaletteData.java
@@ -0,0 +0,0 @@
+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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/fluid/FluidFluidState.java
@@ -0,0 +0,0 @@
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_getblock/GetBlockChunk.java
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/getblock/GetBlockChunk.java
@@ -0,0 +0,0 @@
-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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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());
}
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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.
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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)));
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
}
}
}
@@ -0,0 +0,0 @@ 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++];
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@
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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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(
@@ -0,0 +0,0 @@ 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),
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/starlight/storage/StarlightSectionData.java
@@ -0,0 +0,0 @@
+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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/core/MappedRegistry.java
+++ b/src/main/java/net/minecraft/core/MappedRegistry.java
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -0,0 +0,0 @@ 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
);
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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() {
@@ -0,0 +0,0 @@ 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
);
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
}
}
}
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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)) {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/util/BitStorage.java
+++ b/src/main/java/net/minecraft/util/BitStorage.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java
+++ b/src/main/java/net/minecraft/util/CrudeIncrementalIntIdentityHashBiMap.java
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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];
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/util/SimpleBitStorage.java
+++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/util/ZeroBitStorage.java
+++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
}
@@ -0,0 +0,0 @@ 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;
}
}
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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);
}
}
@@ -0,0 +0,0 @@ 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));
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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();
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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) -> {
@@ -0,0 +0,0 @@ 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();
}
@@ -0,0 +0,0 @@ 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;
}
}
}
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java
+++ b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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>() {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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()) {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
+++ b/src/main/java/net/minecraft/world/level/chunk/HashMapPalette.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -0,0 +0,0 @@ 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() {
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -0,0 +0,0 @@ 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() {
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
}
}
- if (!fluid.isEmpty()) {
+ if (!!fluid.isRandomlyTicking()) { // Paper - block counting
--this.tickingFluidCount;
}
@@ -0,0 +0,0 @@ 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;
}
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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;
}
}
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LinearPalette.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LinearPalette.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/Palette.java
+++ b/src/main/java/net/minecraft/world/level/chunk/Palette.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -0,0 +0,0 @@ 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,
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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();
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/chunk/SingleValuePalette.java
+++ b/src/main/java/net/minecraft/world/level/chunk/SingleValuePalette.java
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
}
}
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
}
@@ -0,0 +0,0 @@ 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])) {
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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
@@ -0,0 +0,0 @@ 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";
}
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/material/FluidState.java
+++ b/src/main/java/net/minecraft/world/level/material/FluidState.java
@@ -0,0 +0,0 @@ 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()
@@ -0,0 +0,0 @@ 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() {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/DiscreteVoxelShape.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java
@@ -0,0 +0,0 @@ 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();
}
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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) {
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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;
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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
@@ -0,0 +0,0 @@ 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);
@@ -0,0 +0,0 @@ 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() {