aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSpottedleaf <[email protected]>2024-10-23 20:45:35 -0700
committerSpottedleaf <[email protected]>2024-10-25 11:25:09 -0700
commit47258a71184e0b3c919e2f224cf2aca985722ddf (patch)
tree8551ac0310ac89f17688e20b31faa656c1137f32
parent6df21e61afabedae7d4888ba5b4a2bb16fe1c264 (diff)
downloadPaper-47258a71184e0b3c919e2f224cf2aca985722ddf.tar.gz
Paper-47258a71184e0b3c919e2f224cf2aca985722ddf.zip
Move common diffs to MCUtils
-rw-r--r--moonrise_update_1_21_2.txt1
-rw-r--r--patches/server/1070-fixup-MC-Utils.patch952
-rw-r--r--patches/server/1073-fixup-Moonrise-optimisation-patches.patch963
3 files changed, 952 insertions, 964 deletions
diff --git a/moonrise_update_1_21_2.txt b/moonrise_update_1_21_2.txt
index 87e5b1546a..f7067cc556 100644
--- a/moonrise_update_1_21_2.txt
+++ b/moonrise_update_1_21_2.txt
@@ -13,7 +13,6 @@ add notes to moonrise patch:
todo:
- double check that the misc changes commit on dev/1.21.2 moonrise is applied
- implement platformhooks
-- move common diff from moonrise patch to mcutil patch
- delete old block state table patch
- in StateHolder, implement getNullableValue from blockstate_propertyaccess
- ChunkEntitySlices getChunkEntities(), callEntitiesLoadEvent(), callEntitiesUnloadEvent()
diff --git a/patches/server/1070-fixup-MC-Utils.patch b/patches/server/1070-fixup-MC-Utils.patch
index 6bca72238a..b68d600395 100644
--- a/patches/server/1070-fixup-MC-Utils.patch
+++ b/patches/server/1070-fixup-MC-Utils.patch
@@ -4,19 +4,700 @@ Date: Mon, 21 Oct 2024 12:21:54 -0700
Subject: [PATCH] fixup! MC Utils
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/PlatformHooks.java b/src/main/java/ca/spottedleaf/moonrise/common/PlatformHooks.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..69a20e9d72b02f28b349f24fd0ea08736888e642
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/moonrise/common/PlatformHooks.java
+@@ -0,0 +1,115 @@
++package ca.spottedleaf.moonrise.common;
++
++import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
++import com.mojang.datafixers.DataFixer;
++import net.minecraft.core.BlockPos;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.server.level.GenerationChunkHolder;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.server.level.ServerPlayer;
++import net.minecraft.util.datafix.DataFixTypes;
++import net.minecraft.world.entity.Entity;
++import net.minecraft.world.level.BlockGetter;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.Level;
++import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.LevelChunk;
++import net.minecraft.world.level.chunk.ProtoChunk;
++import net.minecraft.world.level.chunk.storage.SerializableChunkData;
++import net.minecraft.world.level.entity.EntityTypeTest;
++import net.minecraft.world.phys.AABB;
++import java.util.List;
++import java.util.ServiceLoader;
++import java.util.function.Predicate;
++
++public interface PlatformHooks {
++ public static PlatformHooks get() {
++ return Holder.INSTANCE;
++ }
++
++ public String getBrand();
++
++ public int getLightEmission(final BlockState blockState, final BlockGetter world, final BlockPos pos);
++
++ public Predicate<BlockState> maybeHasLightEmission();
++
++ public boolean hasCurrentlyLoadingChunk();
++
++ public LevelChunk getCurrentlyLoadingChunk(final GenerationChunkHolder holder);
++
++ public void setCurrentlyLoading(final GenerationChunkHolder holder, final LevelChunk levelChunk);
++
++ public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original);
++
++ public boolean allowAsyncTicketUpdates();
++
++ public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel);
++
++ public void chunkUnloadFromWorld(final LevelChunk chunk);
++
++ public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data);
++
++ public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
++
++ public void onChunkUnWatch(final ServerLevel world, final ChunkPos chunk, final ServerPlayer player);
++
++ public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
++ final List<Entity> into);
++
++ public <T extends Entity> void addToGetEntities(final Level world, final EntityTypeTest<Entity, T> entityTypeTest,
++ final AABB boundingBox, final Predicate<? super T> predicate,
++ final List<? super T> into, final int maxCount);
++
++ public void entityMove(final Entity entity, final long oldSection, final long newSection);
++
++ public boolean screenEntity(final ServerLevel world, final Entity entity, final boolean fromDisk, final boolean event);
++
++ public boolean configFixMC224294();
++
++ public boolean configAutoConfigSendDistance();
++
++ public double configPlayerMaxLoadRate();
++
++ public double configPlayerMaxGenRate();
++
++ public double configPlayerMaxSendRate();
++
++ public int configPlayerMaxConcurrentLoads();
++
++ public int configPlayerMaxConcurrentGens();
++
++ public long configAutoSaveInterval();
++
++ public int configMaxAutoSavePerTick();
++
++ public boolean configFixMC159283();
++
++ // support for CB chunk mustNotSave
++ public boolean forceNoSave(final ChunkAccess chunk);
++
++ public CompoundTag convertNBT(final DataFixTypes type, final DataFixer dataFixer, final CompoundTag nbt,
++ final int fromVersion, final int toVersion);
++
++ public boolean hasMainChunkLoadHook();
++
++ public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData);
++
++ public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities);
++
++ public void unloadEntity(final Entity entity);
++
++ public int modifyEntityTrackingRange(final Entity entity, final int currentRange);
++
++ public static final class Holder {
++ private Holder() {
++ }
++
++ private static final PlatformHooks INSTANCE;
++
++ static {
++ INSTANCE = ServiceLoader.load(PlatformHooks.class, PlatformHooks.class.getClassLoader()).findFirst()
++ .orElseThrow(() -> new RuntimeException("Failed to locate PlatformHooks"));
++ }
++ }
++}
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java
+index ba68998f6ef57b24c72fd833bd7de440de9501cc..7fed43a1e7bcf35c4d7fd3224837a47fedd59860 100644
+--- a/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java
++++ b/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java
+@@ -13,15 +13,15 @@ import java.util.NoSuchElementException;
+ */
+ public final class EntityList implements Iterable<Entity> {
+
+- protected final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
++ private final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
+ {
+ this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
+ }
+
+- protected static final Entity[] EMPTY_LIST = new Entity[0];
++ private static final Entity[] EMPTY_LIST = new Entity[0];
+
+- protected Entity[] entities = EMPTY_LIST;
+- protected int count;
++ private Entity[] entities = EMPTY_LIST;
++ private int count;
+
+ public int size() {
+ return this.count;
+@@ -94,10 +94,9 @@ public final class EntityList implements Iterable<Entity> {
+
+ @Override
+ public Iterator<Entity> iterator() {
+- return new Iterator<Entity>() {
+-
+- Entity lastRet;
+- int current;
++ return new Iterator<>() {
++ private Entity lastRet;
++ private int current;
+
+ @Override
+ public boolean hasNext() {
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/IBlockDataList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IBlockDataList.java
+deleted file mode 100644
+index fcfbca333234c09f7c056bbfcd9ac8860b20a8db..0000000000000000000000000000000000000000
+--- a/src/main/java/ca/spottedleaf/moonrise/common/list/IBlockDataList.java
++++ /dev/null
+@@ -1,125 +0,0 @@
+-package ca.spottedleaf.moonrise.common.list;
+-
+-import it.unimi.dsi.fastutil.longs.LongIterator;
+-import it.unimi.dsi.fastutil.shorts.Short2LongOpenHashMap;
+-import java.util.Arrays;
+-import net.minecraft.world.level.block.Block;
+-import net.minecraft.world.level.block.state.BlockState;
+-import net.minecraft.world.level.chunk.GlobalPalette;
+-
+-public final class IBlockDataList {
+-
+- private static final GlobalPalette<BlockState> GLOBAL_PALETTE = new GlobalPalette<>(Block.BLOCK_STATE_REGISTRY);
+-
+- // map of location -> (index | (location << 16) | (palette id << 32))
+- private final Short2LongOpenHashMap map = new Short2LongOpenHashMap(2, 0.8f);
+- {
+- this.map.defaultReturnValue(Long.MAX_VALUE);
+- }
+-
+- private static final long[] EMPTY_LIST = new long[0];
+-
+- private long[] byIndex = EMPTY_LIST;
+- private int size;
+-
+- public static int getLocationKey(final int x, final int y, final int z) {
+- return (x & 15) | (((z & 15) << 4)) | ((y & 255) << (4 + 4));
+- }
+-
+- public static BlockState getBlockDataFromRaw(final long raw) {
+- return GLOBAL_PALETTE.valueFor((int)(raw >>> 32));
+- }
+-
+- public static int getIndexFromRaw(final long raw) {
+- return (int)(raw & 0xFFFF);
+- }
+-
+- public static int getLocationFromRaw(final long raw) {
+- return (int)((raw >>> 16) & 0xFFFF);
+- }
+-
+- public static long getRawFromValues(final int index, final int location, final BlockState data) {
+- return (long)index | ((long)location << 16) | (((long)GLOBAL_PALETTE.idFor(data)) << 32);
+- }
+-
+- public static long setIndexRawValues(final long value, final int index) {
+- return value & ~(0xFFFF) | (index);
+- }
+-
+- public long add(final int x, final int y, final int z, final BlockState data) {
+- return this.add(getLocationKey(x, y, z), data);
+- }
+-
+- public long add(final int location, final BlockState data) {
+- final long curr = this.map.get((short)location);
+-
+- if (curr == Long.MAX_VALUE) {
+- final int index = this.size++;
+- final long raw = getRawFromValues(index, location, data);
+- this.map.put((short)location, raw);
+-
+- if (index >= this.byIndex.length) {
+- this.byIndex = Arrays.copyOf(this.byIndex, (int)Math.max(4L, this.byIndex.length * 2L));
+- }
+-
+- this.byIndex[index] = raw;
+- return raw;
+- } else {
+- final int index = getIndexFromRaw(curr);
+- final long raw = this.byIndex[index] = getRawFromValues(index, location, data);
+-
+- this.map.put((short)location, raw);
+-
+- return raw;
+- }
+- }
+-
+- public long remove(final int x, final int y, final int z) {
+- return this.remove(getLocationKey(x, y, z));
+- }
+-
+- public long remove(final int location) {
+- final long ret = this.map.remove((short)location);
+- final int index = getIndexFromRaw(ret);
+- if (ret == Long.MAX_VALUE) {
+- return ret;
+- }
+-
+- // move the entry at the end to this index
+- final int endIndex = --this.size;
+- final long end = this.byIndex[endIndex];
+- if (index != endIndex) {
+- // not empty after this call
+- this.map.put((short)getLocationFromRaw(end), setIndexRawValues(end, index));
+- }
+- this.byIndex[index] = end;
+- this.byIndex[endIndex] = 0L;
+-
+- return ret;
+- }
+-
+- public int size() {
+- return this.size;
+- }
+-
+- public long getRaw(final int index) {
+- return this.byIndex[index];
+- }
+-
+- public int getLocation(final int index) {
+- return getLocationFromRaw(this.getRaw(index));
+- }
+-
+- public BlockState getData(final int index) {
+- return getBlockDataFromRaw(this.getRaw(index));
+- }
+-
+- public void clear() {
+- this.size = 0;
+- this.map.clear();
+- }
+-
+- public LongIterator getRawIterator() {
+- return this.map.values().iterator();
+- }
+-}
+\ No newline at end of file
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/IntList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IntList.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9f3b25bb2439f283f878db93973a02fcdcd14eed
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/moonrise/common/list/IntList.java
+@@ -0,0 +1,77 @@
++package ca.spottedleaf.moonrise.common.list;
++
++import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
++import java.util.Arrays;
++
++public final class IntList {
++
++ private final Int2IntOpenHashMap map = new Int2IntOpenHashMap();
++ {
++ this.map.defaultReturnValue(Integer.MIN_VALUE);
++ }
++
++ private static final int[] EMPTY_LIST = new int[0];
++
++ private int[] byIndex = EMPTY_LIST;
++ private int count;
++
++ public int size() {
++ return this.count;
++ }
++
++ public void setMinCapacity(final int len) {
++ final int[] byIndex = this.byIndex;
++ if (byIndex.length < len) {
++ this.byIndex = Arrays.copyOf(byIndex, len);
++ }
++ }
++
++ public int getRaw(final int index) {
++ return this.byIndex[index];
++ }
++
++ public boolean add(final int value) {
++ final int count = this.count;
++ final int currIndex = this.map.putIfAbsent(value, count);
++
++ if (currIndex != Integer.MIN_VALUE) {
++ return false; // already in this list
++ }
++
++ int[] list = this.byIndex;
++
++ if (list.length == count) {
++ // resize required
++ list = this.byIndex = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
++ }
++
++ list[count] = value;
++ this.count = count + 1;
++
++ return true;
++ }
++
++ public boolean remove(final int value) {
++ final int index = this.map.remove(value);
++ if (index == Integer.MIN_VALUE) {
++ return false;
++ }
++
++ // move the entry at the end to this index
++ final int endIndex = --this.count;
++ final int end = this.byIndex[endIndex];
++ if (index != endIndex) {
++ // not empty after this call
++ this.map.put(end, index);
++ }
++ this.byIndex[index] = end;
++ this.byIndex[endIndex] = 0;
++
++ return true;
++ }
++
++ public void clear() {
++ this.count = 0;
++ this.map.clear();
++ }
++}
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..2bae9949ef325d0001aa638150fbbdf968367e75
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java
+@@ -0,0 +1,77 @@
++package ca.spottedleaf.moonrise.common.list;
++
++import it.unimi.dsi.fastutil.shorts.Short2ShortOpenHashMap;
++import java.util.Arrays;
++
++public final class ShortList {
++
++ private final Short2ShortOpenHashMap map = new Short2ShortOpenHashMap();
++ {
++ this.map.defaultReturnValue(Short.MIN_VALUE);
++ }
++
++ private static final short[] EMPTY_LIST = new short[0];
++
++ private short[] byIndex = EMPTY_LIST;
++ private short count;
++
++ public int size() {
++ return (int)this.count;
++ }
++
++ public short getRaw(final int index) {
++ return this.byIndex[index];
++ }
++
++ public void setMinCapacity(final int len) {
++ final short[] byIndex = this.byIndex;
++ if (byIndex.length < len) {
++ this.byIndex = Arrays.copyOf(byIndex, len);
++ }
++ }
++
++ public boolean add(final short value) {
++ final int count = (int)this.count;
++ final short currIndex = this.map.putIfAbsent(value, (short)count);
++
++ if (currIndex != Short.MIN_VALUE) {
++ return false; // already in this list
++ }
++
++ short[] list = this.byIndex;
++
++ if (list.length == count) {
++ // resize required
++ list = this.byIndex = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
++ }
++
++ list[count] = value;
++ this.count = (short)(count + 1);
++
++ return true;
++ }
++
++ public boolean remove(final short value) {
++ final short index = this.map.remove(value);
++ if (index == Short.MIN_VALUE) {
++ return false;
++ }
++
++ // move the entry at the end to this index
++ final short endIndex = --this.count;
++ final short end = this.byIndex[endIndex];
++ if (index != endIndex) {
++ // not empty after this call
++ this.map.put(end, index);
++ }
++ this.byIndex[(int)index] = end;
++ this.byIndex[(int)endIndex] = (short)0;
++
++ return true;
++ }
++
++ public void clear() {
++ this.count = (short)0;
++ this.map.clear();
++ }
++}
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/LazyRunnable.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/LazyRunnable.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..c2d917c2eac55b8a4411a6e159f177f9428b1150
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/LazyRunnable.java
+@@ -0,0 +1,22 @@
++package ca.spottedleaf.moonrise.common.misc;
++
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import java.lang.invoke.VarHandle;
++
++public final class LazyRunnable implements Runnable {
++
++ private volatile Runnable toRun;
++ private static final VarHandle TO_RUN_HANDLE = ConcurrentUtil.getVarHandle(LazyRunnable.class, "toRun", Runnable.class);
++
++ public void setRunnable(final Runnable run) {
++ final Runnable prev = (Runnable)TO_RUN_HANDLE.compareAndExchange(this, (Runnable)null, run);
++ if (prev != null) {
++ throw new IllegalStateException("Runnable already set");
++ }
++ }
++
++ @Override
++ public void run() {
++ ((Runnable)TO_RUN_HANDLE.getVolatile(this)).run();
++ }
++}
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
+index ab093b0e8ac6f762921eb1d15f5217345c4eba05..bb44de17a37082e57f2292a4f470740be1d09b11 100644
+--- a/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
++++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
+@@ -4,13 +4,17 @@ import ca.spottedleaf.moonrise.common.list.ReferenceList;
+ import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
+ import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
+ import ca.spottedleaf.moonrise.common.util.ChunkSystem;
++import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
++import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
+ import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
++import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
+ import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
+ import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
+ import net.minecraft.core.BlockPos;
+ import net.minecraft.server.level.ServerLevel;
+ import net.minecraft.server.level.ServerPlayer;
+ import net.minecraft.world.level.ChunkPos;
++import java.util.ArrayList;
+
+ public final class NearbyPlayers {
+
+@@ -20,7 +24,27 @@ public final class NearbyPlayers {
+ GENERAL_REALLY_SMALL,
+ TICK_VIEW_DISTANCE,
+ VIEW_DISTANCE,
+- SPAWN_RANGE, // Moonrise - chunk tick iteration
++ // Moonrise start - chunk tick iteration
++ SPAWN_RANGE {
++ @Override
++ void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
++ ((ChunkTickServerLevel)world).moonrise$addPlayerTickingRequest(chunkX, chunkZ);
++ }
++
++ @Override
++ void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
++ ((ChunkTickServerLevel)world).moonrise$removePlayerTickingRequest(chunkX, chunkZ);
++ }
++ };
++ // Moonrise end - chunk tick iteration
++
++ void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
++
++ }
++
++ void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
++
++ }
+ }
+
+ private static final NearbyMapType[] MAP_TYPES = NearbyMapType.values();
+@@ -37,6 +61,12 @@ public final class NearbyPlayers {
+ private final ServerLevel world;
+ private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
+ private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
++ private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES];
++ {
++ for (int i = 0; i < this.directByChunk.length; ++i) {
++ this.directByChunk[i] = new Long2ReferenceOpenHashMap<>();
++ }
++ }
+
+ public NearbyPlayers(final ServerLevel world) {
+ this.world = world;
+@@ -70,6 +100,16 @@ public final class NearbyPlayers {
+ }
+ }
+
++ public void clear() {
++ if (this.players.isEmpty()) {
++ return;
++ }
++
++ for (final ServerPlayer player : new ArrayList<>(this.players.keySet())) {
++ this.removePlayer(player);
++ }
++ }
++
+ public void tickPlayer(final ServerPlayer player) {
+ final TrackedPlayer[] players = this.players.get(player);
+ if (players == null) {
+@@ -94,38 +134,41 @@ public final class NearbyPlayers {
+ return this.byChunk.get(CoordinateUtils.getChunkKey(pos));
+ }
+
+- public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
+- final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
++ public TrackedChunk getChunk(final int chunkX, final int chunkZ) {
++ return this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ }
+
+- return chunk == null ? null : chunk.players[type.ordinal()];
++ public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
++ return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
+ }
+
+ public ReferenceList<ServerPlayer> getPlayers(final ChunkPos pos, final NearbyMapType type) {
+- final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
+-
+- return chunk == null ? null : chunk.players[type.ordinal()];
++ return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
+ }
+
+ public ReferenceList<ServerPlayer> getPlayersByChunk(final int chunkX, final int chunkZ, final NearbyMapType type) {
+- final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
+-
+- return chunk == null ? null : chunk.players[type.ordinal()];
++ return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
+ }
+
+ public ReferenceList<ServerPlayer> getPlayersByBlock(final int blockX, final int blockZ, final NearbyMapType type) {
+- final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
+-
+- return chunk == null ? null : chunk.players[type.ordinal()];
++ return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
+ }
+
+ public static final class TrackedChunk {
+
+ private static final ServerPlayer[] EMPTY_PLAYERS_ARRAY = new ServerPlayer[0];
+
++ private final long chunkKey;
++ private final NearbyPlayers nearbyPlayers;
+ private final ReferenceList<ServerPlayer>[] players = new ReferenceList[TOTAL_MAP_TYPES];
+ private int nonEmptyLists;
+ private long updateCount;
+
++ public TrackedChunk(final long chunkKey, final NearbyPlayers nearbyPlayers) {
++ this.chunkKey = chunkKey;
++ this.nearbyPlayers = nearbyPlayers;
++ }
++
+ public boolean isEmpty() {
+ return this.nonEmptyLists == 0;
+ }
+@@ -145,7 +188,9 @@ public final class NearbyPlayers {
+ final ReferenceList<ServerPlayer> list = this.players[idx];
+ if (list == null) {
+ ++this.nonEmptyLists;
+- (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY)).add(player);
++ final ReferenceList<ServerPlayer> players = (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY));
++ this.nearbyPlayers.directByChunk[idx].put(this.chunkKey, players);
++ players.add(player);
+ return;
+ }
+
+@@ -169,6 +214,7 @@ public final class NearbyPlayers {
+
+ if (list.size() == 0) {
+ this.players[idx] = null;
++ this.nearbyPlayers.directByChunk[idx].remove(this.chunkKey);
+ --this.nonEmptyLists;
+ }
+ }
+@@ -187,9 +233,19 @@ public final class NearbyPlayers {
+ protected void addCallback(final ServerPlayer parameter, final int chunkX, final int chunkZ) {
+ final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
+
+- NearbyPlayers.this.byChunk.computeIfAbsent(chunkKey, (final long keyInMap) -> {
+- return new TrackedChunk();
+- }).addPlayer(parameter, this.type);
++ final TrackedChunk chunk = NearbyPlayers.this.byChunk.get(chunkKey);
++ final NearbyMapType type = this.type;
++ if (chunk != null) {
++ chunk.addPlayer(parameter, type);
++ type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
++ } else {
++ final TrackedChunk created = new TrackedChunk(chunkKey, NearbyPlayers.this);
++ NearbyPlayers.this.byChunk.put(chunkKey, created);
++ created.addPlayer(parameter, type);
++ type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
++
++ ((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$requestChunkData(chunkKey).nearbyPlayers = created;
++ }
+ }
+
+ @Override
+@@ -201,10 +257,16 @@ public final class NearbyPlayers {
+ throw new IllegalStateException("Chunk should exist at " + new ChunkPos(chunkKey));
+ }
+
+- chunk.removePlayer(parameter, this.type);
++ final NearbyMapType type = this.type;
++ chunk.removePlayer(parameter, type);
++ type.removeFrom(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
+
+ if (chunk.isEmpty()) {
+ NearbyPlayers.this.byChunk.remove(chunkKey);
++ final ChunkData chunkData = ((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$releaseChunkData(chunkKey);
++ if (chunkData != null) {
++ chunkData.nearbyPlayers = null;
++ }
+ }
+ }
+ }
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 0abba00741b39b69a7f167e5d2670f2565c9a752..96bbab283eb6c7e7863383fea0ddc62510391091 100644
+index 0abba00741b39b69a7f167e5d2670f2565c9a752..b61611351bf23efc1e90bab8a850ebbe6ffdd516 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java
-@@ -1,6 +1,6 @@
+@@ -1,6 +1,7 @@
package ca.spottedleaf.moonrise.common.util;
-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
+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.player.RegionizedPlayerChunkLoader;
-@@ -23,27 +23,27 @@ public final class ChunkSystem {
+@@ -23,27 +24,27 @@ public final class ChunkSystem {
private static final Logger LOGGER = LogUtils.getLogger();
public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
@@ -49,15 +730,278 @@ index 0abba00741b39b69a7f167e5d2670f2565c9a752..96bbab283eb6c7e7863383fea0ddc625
((ChunkSystemServerLevel)level).moonrise$getChunkTaskScheduler().scheduleTickingState(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
}
-@@ -67,7 +67,7 @@ public final class ChunkSystem {
+@@ -67,7 +68,10 @@ public final class ChunkSystem {
return getUpdatingChunkHolderCount(level) != 0;
}
- public static boolean screenEntity(final ServerLevel level, final Entity entity) {
+ public static boolean screenEntity(final ServerLevel level, final Entity entity, final boolean fromDisk, final boolean event) {
++ if (!PlatformHooks.get().screenEntity(level, entity, fromDisk, event)) {
++ return false;
++ }
return true;
}
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MixinWorkarounds.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MixinWorkarounds.java
+index ac6f284ee4469d16c5655328b2488d7612832353..97848869df61648fc415e4d39f409f433202c274 100644
+--- a/src/main/java/ca/spottedleaf/moonrise/common/util/MixinWorkarounds.java
++++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MixinWorkarounds.java
+@@ -3,8 +3,12 @@ package ca.spottedleaf.moonrise.common.util;
+ public final class MixinWorkarounds {
+
+ // mixins tries to find the owner of the clone() method, which doesn't exist and NPEs
++ // https://github.com/FabricMC/Mixin/pull/147
+ public static long[] clone(final long[] values) {
+ return values.clone();
+ }
+
++ public static byte[] clone(final byte[] values) {
++ return values.clone();
++ }
+ }
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
+index 3abe0bd2a820352b85306d554bf14a4cf6123091..c125c70a68130be373acc989053a6c0e487be924 100644
+--- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
++++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
+@@ -1,45 +1,100 @@
+ package ca.spottedleaf.moonrise.common.util;
+
+-import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadPool;
++import ca.spottedleaf.concurrentutil.executor.thread.PrioritisedThreadPool;
++import ca.spottedleaf.moonrise.common.PlatformHooks;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+-import java.io.File;
++import java.util.concurrent.TimeUnit;
++import java.util.concurrent.atomic.AtomicInteger;
++import java.util.function.Consumer;
+
+ public final class MoonriseCommon {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MoonriseCommon.class);
+
+- // Paper start
+- public static PrioritisedThreadPool WORKER_POOL;
+- public static int WORKER_THREADS;
+- public static void init(io.papermc.paper.configuration.GlobalConfiguration.ChunkSystem chunkSystem) {
+- // Paper end
++ public static final PrioritisedThreadPool WORKER_POOL = new PrioritisedThreadPool(
++ new Consumer<>() {
++ private final AtomicInteger idGenerator = new AtomicInteger();
++
++ @Override
++ public void accept(Thread thread) {
++ thread.setDaemon(true);
++ thread.setName(PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement());
++ thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
++ @Override
++ public void uncaughtException(final Thread thread, final Throwable throwable) {
++ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
++ }
++ });
++ }
++ }
++ );
++ public static final long WORKER_QUEUE_HOLD_TIME = (long)(20.0e6); // 20ms
++ public static final int CLIENT_DIVISION = 0;
++ public static final PrioritisedThreadPool.ExecutorGroup RENDER_EXECUTOR_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(CLIENT_DIVISION, 0);
++ public static final int SERVER_DIVISION = 1;
++ public static final PrioritisedThreadPool.ExecutorGroup PARALLEL_GEN_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
++ public static final PrioritisedThreadPool.ExecutorGroup RADIUS_AWARE_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
++ public static final PrioritisedThreadPool.ExecutorGroup LOAD_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
++
++ public static void adjustWorkerThreads(final int configWorkerThreads, final int configIoThreads) {
+ int defaultWorkerThreads = Runtime.getRuntime().availableProcessors() / 2;
+ if (defaultWorkerThreads <= 4) {
+ defaultWorkerThreads = defaultWorkerThreads <= 3 ? 1 : 2;
+ } else {
+ defaultWorkerThreads = defaultWorkerThreads / 2;
+ }
+- defaultWorkerThreads = Integer.getInteger("Paper.WorkerThreadCount", Integer.valueOf(defaultWorkerThreads)); // Paper
++ defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
+
+- int workerThreads = chunkSystem.workerThreads; // Paper
++ int workerThreads = configWorkerThreads;
+
+ if (workerThreads <= 0) {
+ workerThreads = defaultWorkerThreads;
+ }
+
+- WORKER_POOL = new PrioritisedThreadPool(
+- "Paper Worker Pool", workerThreads, // Paper
+- (final Thread thread, final Integer id) -> {
+- thread.setName("Paper Common Worker #" + id.intValue()); // Paper
++ final int ioThreads = Math.max(1, configIoThreads);
++
++ WORKER_POOL.adjustThreadCount(workerThreads);
++ IO_POOL.adjustThreadCount(ioThreads);
++
++ LOGGER.info(PlatformHooks.get().getBrand() + " is using " + workerThreads + " worker threads, " + ioThreads + " I/O threads");
++ }
++
++ public static final PrioritisedThreadPool IO_POOL = new PrioritisedThreadPool(
++ new Consumer<>() {
++ private final AtomicInteger idGenerator = new AtomicInteger();
++
++ @Override
++ public void accept(final Thread thread) {
++ thread.setDaemon(true);
++ thread.setName(PlatformHooks.get().getBrand() + " I/O Worker #" + this.idGenerator.getAndIncrement());
+ thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(final Thread thread, final Throwable throwable) {
+ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
+ }
+ });
+- }, (long)(20.0e6)); // 20ms
+- WORKER_THREADS = workerThreads;
++ }
++ }
++ );
++ public static final long IO_QUEUE_HOLD_TIME = (long)(100.0e6); // 100ms
++ public static final PrioritisedThreadPool.ExecutorGroup CLIENT_PROFILER_IO_GROUP = IO_POOL.createExecutorGroup(CLIENT_DIVISION, 0);
++ public static final PrioritisedThreadPool.ExecutorGroup SERVER_REGION_IO_GROUP = IO_POOL.createExecutorGroup(SERVER_DIVISION, 0);
++
++ public static void haltExecutors() {
++ MoonriseCommon.WORKER_POOL.shutdown(false);
++ LOGGER.info("Awaiting termination of worker pool for up to 60s...");
++ if (!MoonriseCommon.WORKER_POOL.join(TimeUnit.SECONDS.toMillis(60L))) {
++ LOGGER.error("Worker pool did not shut down in time!");
++ MoonriseCommon.WORKER_POOL.halt(false);
++ }
++
++ MoonriseCommon.IO_POOL.shutdown(false);
++ LOGGER.info("Awaiting termination of I/O pool for up to 60s...");
++ if (!MoonriseCommon.IO_POOL.join(TimeUnit.SECONDS.toMillis(60L))) {
++ LOGGER.error("I/O pool did not shut down in time!");
++ MoonriseCommon.IO_POOL.halt(false);
++ }
+ }
+
+ private MoonriseCommon() {}
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
+index 1cf32d7d1bbc8a0a3f7cb9024c793f6744199f64..559c959aff3c9deef867b9e425fba3e2e669cac6 100644
+--- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
++++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
+@@ -1,8 +1,10 @@
+ package ca.spottedleaf.moonrise.common.util;
+
++import ca.spottedleaf.moonrise.common.PlatformHooks;
++
+ public final class MoonriseConstants {
+
+- public static final int MAX_VIEW_DISTANCE = 32;
++ public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
+
+ private MoonriseConstants() {}
+
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java b/src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a9ff1c1a70faf4b7a64b265932f07a8b8f00c1ff
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java
+@@ -0,0 +1,52 @@
++package ca.spottedleaf.moonrise.common.util;
++
++import net.minecraft.world.level.levelgen.LegacyRandomSource;
++
++/**
++ * Avoid costly CAS of superclass
++ */
++public final class SimpleRandom extends LegacyRandomSource {
++
++ private static final long MULTIPLIER = 25214903917L;
++ private static final long ADDEND = 11L;
++ private static final int BITS = 48;
++ private static final long MASK = (1L << BITS) - 1;
++
++ private long value;
++
++ public SimpleRandom(final long seed) {
++ super(0L);
++ this.value = seed;
++ }
++
++ @Override
++ public void setSeed(final long seed) {
++ this.value = (seed ^ MULTIPLIER) & MASK;
++ }
++
++ private long advanceSeed() {
++ return this.value = ((this.value * MULTIPLIER) + ADDEND) & MASK;
++ }
++
++ @Override
++ public int next(final int bits) {
++ return (int)(this.advanceSeed() >>> (BITS - bits));
++ }
++
++ @Override
++ public int nextInt() {
++ final long seed = this.advanceSeed();
++ return (int)(seed >>> (BITS - Integer.SIZE));
++ }
++
++ @Override
++ public int nextInt(final int bound) {
++ if (bound <= 0) {
++ throw new IllegalArgumentException();
++ }
++
++ // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
++ final long value = this.advanceSeed() >>> (BITS - Integer.SIZE);
++ return (int)((value * (long)bound) >>> Integer.SIZE);
++ }
++}
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
+index 11b7f15755dde766140c29bedca456c80d53293f..217d1f908a36a5177ba3cbb80a33f73d4dab0fa0 100644
+--- a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
++++ b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
+@@ -77,11 +77,15 @@ public class TickThread extends Thread {
+ }
+
+ public TickThread(final Runnable run, final String name) {
+- this(run, name, ID_GENERATOR.incrementAndGet());
++ this(null, run, name);
+ }
+
+- private TickThread(final Runnable run, final String name, final int id) {
+- super(run, name);
++ public TickThread(final ThreadGroup group, final Runnable run, final String name) {
++ this(group, run, name, ID_GENERATOR.incrementAndGet());
++ }
++
++ private TickThread(final ThreadGroup group, final Runnable run, final String name, final int id) {
++ super(group, run, name);
+ this.id = id;
+ }
+
+diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/WorldUtil.java b/src/main/java/ca/spottedleaf/moonrise/common/util/WorldUtil.java
+index af9623240ff2d389aa7090623f507720e7dbab7d..efda2688ae1254a82ba7f6bf8bf597ef224cbb86 100644
+--- a/src/main/java/ca/spottedleaf/moonrise/common/util/WorldUtil.java
++++ b/src/main/java/ca/spottedleaf/moonrise/common/util/WorldUtil.java
+@@ -8,11 +8,19 @@ public final class WorldUtil {
+ // min, max are inclusive
+
+ public static int getMaxSection(final LevelHeightAccessor world) {
+- return world.getMaxSection() - 1; // getMaxSection() is exclusive
++ return world.getMaxSectionY();
++ }
++
++ public static int getMaxSection(final Level world) {
++ return world.getMaxSectionY();
+ }
+
+ public static int getMinSection(final LevelHeightAccessor world) {
+- return world.getMinSection();
++ return world.getMinSectionY();
++ }
++
++ public static int getMinSection(final Level world) {
++ return world.getMinSectionY();
+ }
+
+ public static int getMaxLightSection(final LevelHeightAccessor world) {
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
index 6baa313b8201ed23193d7885c85606b0899ade3c..3eb38271b6ca26099b2da04c2d969e32fd72b2af 100644
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
diff --git a/patches/server/1073-fixup-Moonrise-optimisation-patches.patch b/patches/server/1073-fixup-Moonrise-optimisation-patches.patch
index 42a3be5122..031f4c876b 100644
--- a/patches/server/1073-fixup-Moonrise-optimisation-patches.patch
+++ b/patches/server/1073-fixup-Moonrise-optimisation-patches.patch
@@ -4,696 +4,11 @@ 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/PlatformHooks.java b/src/main/java/ca/spottedleaf/moonrise/common/PlatformHooks.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..69a20e9d72b02f28b349f24fd0ea08736888e642
---- /dev/null
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/PlatformHooks.java
-@@ -0,0 +1,115 @@
-+package ca.spottedleaf.moonrise.common;
-+
-+import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
-+import com.mojang.datafixers.DataFixer;
-+import net.minecraft.core.BlockPos;
-+import net.minecraft.nbt.CompoundTag;
-+import net.minecraft.server.level.GenerationChunkHolder;
-+import net.minecraft.server.level.ServerLevel;
-+import net.minecraft.server.level.ServerPlayer;
-+import net.minecraft.util.datafix.DataFixTypes;
-+import net.minecraft.world.entity.Entity;
-+import net.minecraft.world.level.BlockGetter;
-+import net.minecraft.world.level.ChunkPos;
-+import net.minecraft.world.level.Level;
-+import net.minecraft.world.level.block.state.BlockState;
-+import net.minecraft.world.level.chunk.ChunkAccess;
-+import net.minecraft.world.level.chunk.LevelChunk;
-+import net.minecraft.world.level.chunk.ProtoChunk;
-+import net.minecraft.world.level.chunk.storage.SerializableChunkData;
-+import net.minecraft.world.level.entity.EntityTypeTest;
-+import net.minecraft.world.phys.AABB;
-+import java.util.List;
-+import java.util.ServiceLoader;
-+import java.util.function.Predicate;
-+
-+public interface PlatformHooks {
-+ public static PlatformHooks get() {
-+ return Holder.INSTANCE;
-+ }
-+
-+ public String getBrand();
-+
-+ public int getLightEmission(final BlockState blockState, final BlockGetter world, final BlockPos pos);
-+
-+ public Predicate<BlockState> maybeHasLightEmission();
-+
-+ public boolean hasCurrentlyLoadingChunk();
-+
-+ public LevelChunk getCurrentlyLoadingChunk(final GenerationChunkHolder holder);
-+
-+ public void setCurrentlyLoading(final GenerationChunkHolder holder, final LevelChunk levelChunk);
-+
-+ public void chunkFullStatusComplete(final LevelChunk newChunk, final ProtoChunk original);
-+
-+ public boolean allowAsyncTicketUpdates();
-+
-+ public void onChunkHolderTicketChange(final ServerLevel world, final NewChunkHolder holder, final int oldLevel, final int newLevel);
-+
-+ public void chunkUnloadFromWorld(final LevelChunk chunk);
-+
-+ public void chunkSyncSave(final ServerLevel world, final ChunkAccess chunk, final SerializableChunkData data);
-+
-+ public void onChunkWatch(final ServerLevel world, final LevelChunk chunk, final ServerPlayer player);
-+
-+ public void onChunkUnWatch(final ServerLevel world, final ChunkPos chunk, final ServerPlayer player);
-+
-+ public void addToGetEntities(final Level world, final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate,
-+ final List<Entity> into);
-+
-+ public <T extends Entity> void addToGetEntities(final Level world, final EntityTypeTest<Entity, T> entityTypeTest,
-+ final AABB boundingBox, final Predicate<? super T> predicate,
-+ final List<? super T> into, final int maxCount);
-+
-+ public void entityMove(final Entity entity, final long oldSection, final long newSection);
-+
-+ public boolean screenEntity(final ServerLevel world, final Entity entity, final boolean fromDisk, final boolean event);
-+
-+ public boolean configFixMC224294();
-+
-+ public boolean configAutoConfigSendDistance();
-+
-+ public double configPlayerMaxLoadRate();
-+
-+ public double configPlayerMaxGenRate();
-+
-+ public double configPlayerMaxSendRate();
-+
-+ public int configPlayerMaxConcurrentLoads();
-+
-+ public int configPlayerMaxConcurrentGens();
-+
-+ public long configAutoSaveInterval();
-+
-+ public int configMaxAutoSavePerTick();
-+
-+ public boolean configFixMC159283();
-+
-+ // support for CB chunk mustNotSave
-+ public boolean forceNoSave(final ChunkAccess chunk);
-+
-+ public CompoundTag convertNBT(final DataFixTypes type, final DataFixer dataFixer, final CompoundTag nbt,
-+ final int fromVersion, final int toVersion);
-+
-+ public boolean hasMainChunkLoadHook();
-+
-+ public void mainChunkLoad(final ChunkAccess chunk, final SerializableChunkData chunkData);
-+
-+ public List<Entity> modifySavedEntities(final ServerLevel world, final int chunkX, final int chunkZ, final List<Entity> entities);
-+
-+ public void unloadEntity(final Entity entity);
-+
-+ public int modifyEntityTrackingRange(final Entity entity, final int currentRange);
-+
-+ public static final class Holder {
-+ private Holder() {
-+ }
-+
-+ private static final PlatformHooks INSTANCE;
-+
-+ static {
-+ INSTANCE = ServiceLoader.load(PlatformHooks.class, PlatformHooks.class.getClassLoader()).findFirst()
-+ .orElseThrow(() -> new RuntimeException("Failed to locate PlatformHooks"));
-+ }
-+ }
-+}
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java
-index ba68998f6ef57b24c72fd833bd7de440de9501cc..7fed43a1e7bcf35c4d7fd3224837a47fedd59860 100644
---- a/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/EntityList.java
-@@ -13,15 +13,15 @@ import java.util.NoSuchElementException;
- */
- public final class EntityList implements Iterable<Entity> {
-
-- protected final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
-+ private final Int2IntOpenHashMap entityToIndex = new Int2IntOpenHashMap(2, 0.8f);
- {
- this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE);
- }
-
-- protected static final Entity[] EMPTY_LIST = new Entity[0];
-+ private static final Entity[] EMPTY_LIST = new Entity[0];
-
-- protected Entity[] entities = EMPTY_LIST;
-- protected int count;
-+ private Entity[] entities = EMPTY_LIST;
-+ private int count;
-
- public int size() {
- return this.count;
-@@ -94,10 +94,9 @@ public final class EntityList implements Iterable<Entity> {
-
- @Override
- public Iterator<Entity> iterator() {
-- return new Iterator<Entity>() {
--
-- Entity lastRet;
-- int current;
-+ return new Iterator<>() {
-+ private Entity lastRet;
-+ private int current;
-
- @Override
- public boolean hasNext() {
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/IBlockDataList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IBlockDataList.java
-deleted file mode 100644
-index fcfbca333234c09f7c056bbfcd9ac8860b20a8db..0000000000000000000000000000000000000000
---- a/src/main/java/ca/spottedleaf/moonrise/common/list/IBlockDataList.java
-+++ /dev/null
-@@ -1,125 +0,0 @@
--package ca.spottedleaf.moonrise.common.list;
--
--import it.unimi.dsi.fastutil.longs.LongIterator;
--import it.unimi.dsi.fastutil.shorts.Short2LongOpenHashMap;
--import java.util.Arrays;
--import net.minecraft.world.level.block.Block;
--import net.minecraft.world.level.block.state.BlockState;
--import net.minecraft.world.level.chunk.GlobalPalette;
--
--public final class IBlockDataList {
--
-- private static final GlobalPalette<BlockState> GLOBAL_PALETTE = new GlobalPalette<>(Block.BLOCK_STATE_REGISTRY);
--
-- // map of location -> (index | (location << 16) | (palette id << 32))
-- private final Short2LongOpenHashMap map = new Short2LongOpenHashMap(2, 0.8f);
-- {
-- this.map.defaultReturnValue(Long.MAX_VALUE);
-- }
--
-- private static final long[] EMPTY_LIST = new long[0];
--
-- private long[] byIndex = EMPTY_LIST;
-- private int size;
--
-- public static int getLocationKey(final int x, final int y, final int z) {
-- return (x & 15) | (((z & 15) << 4)) | ((y & 255) << (4 + 4));
-- }
--
-- public static BlockState getBlockDataFromRaw(final long raw) {
-- return GLOBAL_PALETTE.valueFor((int)(raw >>> 32));
-- }
--
-- public static int getIndexFromRaw(final long raw) {
-- return (int)(raw & 0xFFFF);
-- }
--
-- public static int getLocationFromRaw(final long raw) {
-- return (int)((raw >>> 16) & 0xFFFF);
-- }
--
-- public static long getRawFromValues(final int index, final int location, final BlockState data) {
-- return (long)index | ((long)location << 16) | (((long)GLOBAL_PALETTE.idFor(data)) << 32);
-- }
--
-- public static long setIndexRawValues(final long value, final int index) {
-- return value & ~(0xFFFF) | (index);
-- }
--
-- public long add(final int x, final int y, final int z, final BlockState data) {
-- return this.add(getLocationKey(x, y, z), data);
-- }
--
-- public long add(final int location, final BlockState data) {
-- final long curr = this.map.get((short)location);
--
-- if (curr == Long.MAX_VALUE) {
-- final int index = this.size++;
-- final long raw = getRawFromValues(index, location, data);
-- this.map.put((short)location, raw);
--
-- if (index >= this.byIndex.length) {
-- this.byIndex = Arrays.copyOf(this.byIndex, (int)Math.max(4L, this.byIndex.length * 2L));
-- }
--
-- this.byIndex[index] = raw;
-- return raw;
-- } else {
-- final int index = getIndexFromRaw(curr);
-- final long raw = this.byIndex[index] = getRawFromValues(index, location, data);
--
-- this.map.put((short)location, raw);
--
-- return raw;
-- }
-- }
--
-- public long remove(final int x, final int y, final int z) {
-- return this.remove(getLocationKey(x, y, z));
-- }
--
-- public long remove(final int location) {
-- final long ret = this.map.remove((short)location);
-- final int index = getIndexFromRaw(ret);
-- if (ret == Long.MAX_VALUE) {
-- return ret;
-- }
--
-- // move the entry at the end to this index
-- final int endIndex = --this.size;
-- final long end = this.byIndex[endIndex];
-- if (index != endIndex) {
-- // not empty after this call
-- this.map.put((short)getLocationFromRaw(end), setIndexRawValues(end, index));
-- }
-- this.byIndex[index] = end;
-- this.byIndex[endIndex] = 0L;
--
-- return ret;
-- }
--
-- public int size() {
-- return this.size;
-- }
--
-- public long getRaw(final int index) {
-- return this.byIndex[index];
-- }
--
-- public int getLocation(final int index) {
-- return getLocationFromRaw(this.getRaw(index));
-- }
--
-- public BlockState getData(final int index) {
-- return getBlockDataFromRaw(this.getRaw(index));
-- }
--
-- public void clear() {
-- this.size = 0;
-- this.map.clear();
-- }
--
-- public LongIterator getRawIterator() {
-- return this.map.values().iterator();
-- }
--}
-\ No newline at end of file
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/IntList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/IntList.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..9f3b25bb2439f283f878db93973a02fcdcd14eed
---- /dev/null
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/IntList.java
-@@ -0,0 +1,77 @@
-+package ca.spottedleaf.moonrise.common.list;
-+
-+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
-+import java.util.Arrays;
-+
-+public final class IntList {
-+
-+ private final Int2IntOpenHashMap map = new Int2IntOpenHashMap();
-+ {
-+ this.map.defaultReturnValue(Integer.MIN_VALUE);
-+ }
-+
-+ private static final int[] EMPTY_LIST = new int[0];
-+
-+ private int[] byIndex = EMPTY_LIST;
-+ private int count;
-+
-+ public int size() {
-+ return this.count;
-+ }
-+
-+ public void setMinCapacity(final int len) {
-+ final int[] byIndex = this.byIndex;
-+ if (byIndex.length < len) {
-+ this.byIndex = Arrays.copyOf(byIndex, len);
-+ }
-+ }
-+
-+ public int getRaw(final int index) {
-+ return this.byIndex[index];
-+ }
-+
-+ public boolean add(final int value) {
-+ final int count = this.count;
-+ final int currIndex = this.map.putIfAbsent(value, count);
-+
-+ if (currIndex != Integer.MIN_VALUE) {
-+ return false; // already in this list
-+ }
-+
-+ int[] list = this.byIndex;
-+
-+ if (list.length == count) {
-+ // resize required
-+ list = this.byIndex = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
-+ }
-+
-+ list[count] = value;
-+ this.count = count + 1;
-+
-+ return true;
-+ }
-+
-+ public boolean remove(final int value) {
-+ final int index = this.map.remove(value);
-+ if (index == Integer.MIN_VALUE) {
-+ return false;
-+ }
-+
-+ // move the entry at the end to this index
-+ final int endIndex = --this.count;
-+ final int end = this.byIndex[endIndex];
-+ if (index != endIndex) {
-+ // not empty after this call
-+ this.map.put(end, index);
-+ }
-+ this.byIndex[index] = end;
-+ this.byIndex[endIndex] = 0;
-+
-+ return true;
-+ }
-+
-+ public void clear() {
-+ this.count = 0;
-+ this.map.clear();
-+ }
-+}
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java b/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..2bae9949ef325d0001aa638150fbbdf968367e75
---- /dev/null
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/list/ShortList.java
-@@ -0,0 +1,77 @@
-+package ca.spottedleaf.moonrise.common.list;
-+
-+import it.unimi.dsi.fastutil.shorts.Short2ShortOpenHashMap;
-+import java.util.Arrays;
-+
-+public final class ShortList {
-+
-+ private final Short2ShortOpenHashMap map = new Short2ShortOpenHashMap();
-+ {
-+ this.map.defaultReturnValue(Short.MIN_VALUE);
-+ }
-+
-+ private static final short[] EMPTY_LIST = new short[0];
-+
-+ private short[] byIndex = EMPTY_LIST;
-+ private short count;
-+
-+ public int size() {
-+ return (int)this.count;
-+ }
-+
-+ public short getRaw(final int index) {
-+ return this.byIndex[index];
-+ }
-+
-+ public void setMinCapacity(final int len) {
-+ final short[] byIndex = this.byIndex;
-+ if (byIndex.length < len) {
-+ this.byIndex = Arrays.copyOf(byIndex, len);
-+ }
-+ }
-+
-+ public boolean add(final short value) {
-+ final int count = (int)this.count;
-+ final short currIndex = this.map.putIfAbsent(value, (short)count);
-+
-+ if (currIndex != Short.MIN_VALUE) {
-+ return false; // already in this list
-+ }
-+
-+ short[] list = this.byIndex;
-+
-+ if (list.length == count) {
-+ // resize required
-+ list = this.byIndex = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
-+ }
-+
-+ list[count] = value;
-+ this.count = (short)(count + 1);
-+
-+ return true;
-+ }
-+
-+ public boolean remove(final short value) {
-+ final short index = this.map.remove(value);
-+ if (index == Short.MIN_VALUE) {
-+ return false;
-+ }
-+
-+ // move the entry at the end to this index
-+ final short endIndex = --this.count;
-+ final short end = this.byIndex[endIndex];
-+ if (index != endIndex) {
-+ // not empty after this call
-+ this.map.put(end, index);
-+ }
-+ this.byIndex[(int)index] = end;
-+ this.byIndex[(int)endIndex] = (short)0;
-+
-+ return true;
-+ }
-+
-+ public void clear() {
-+ this.count = (short)0;
-+ this.map.clear();
-+ }
-+}
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/LazyRunnable.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/LazyRunnable.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..c2d917c2eac55b8a4411a6e159f177f9428b1150
---- /dev/null
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/LazyRunnable.java
-@@ -0,0 +1,22 @@
-+package ca.spottedleaf.moonrise.common.misc;
-+
-+import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
-+import java.lang.invoke.VarHandle;
-+
-+public final class LazyRunnable implements Runnable {
-+
-+ private volatile Runnable toRun;
-+ private static final VarHandle TO_RUN_HANDLE = ConcurrentUtil.getVarHandle(LazyRunnable.class, "toRun", Runnable.class);
-+
-+ public void setRunnable(final Runnable run) {
-+ final Runnable prev = (Runnable)TO_RUN_HANDLE.compareAndExchange(this, (Runnable)null, run);
-+ if (prev != null) {
-+ throw new IllegalStateException("Runnable already set");
-+ }
-+ }
-+
-+ @Override
-+ public void run() {
-+ ((Runnable)TO_RUN_HANDLE.getVolatile(this)).run();
-+ }
-+}
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java b/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
-index ab093b0e8ac6f762921eb1d15f5217345c4eba05..bb44de17a37082e57f2292a4f470740be1d09b11 100644
---- a/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/misc/NearbyPlayers.java
-@@ -4,13 +4,17 @@ import ca.spottedleaf.moonrise.common.list.ReferenceList;
- import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
- import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
- import ca.spottedleaf.moonrise.common.util.ChunkSystem;
-+import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel;
-+import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkData;
- import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants;
-+import ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickServerLevel;
- import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
- import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
- import net.minecraft.core.BlockPos;
- import net.minecraft.server.level.ServerLevel;
- import net.minecraft.server.level.ServerPlayer;
- import net.minecraft.world.level.ChunkPos;
-+import java.util.ArrayList;
-
- public final class NearbyPlayers {
-
-@@ -20,7 +24,27 @@ public final class NearbyPlayers {
- GENERAL_REALLY_SMALL,
- TICK_VIEW_DISTANCE,
- VIEW_DISTANCE,
-- SPAWN_RANGE, // Moonrise - chunk tick iteration
-+ // Moonrise start - chunk tick iteration
-+ SPAWN_RANGE {
-+ @Override
-+ void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
-+ ((ChunkTickServerLevel)world).moonrise$addPlayerTickingRequest(chunkX, chunkZ);
-+ }
-+
-+ @Override
-+ void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
-+ ((ChunkTickServerLevel)world).moonrise$removePlayerTickingRequest(chunkX, chunkZ);
-+ }
-+ };
-+ // Moonrise end - chunk tick iteration
-+
-+ void addTo(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
-+
-+ }
-+
-+ void removeFrom(final ServerPlayer player, final ServerLevel world, final int chunkX, final int chunkZ) {
-+
-+ }
- }
-
- private static final NearbyMapType[] MAP_TYPES = NearbyMapType.values();
-@@ -37,6 +61,12 @@ public final class NearbyPlayers {
- private final ServerLevel world;
- private final Reference2ReferenceOpenHashMap<ServerPlayer, TrackedPlayer[]> players = new Reference2ReferenceOpenHashMap<>();
- private final Long2ReferenceOpenHashMap<TrackedChunk> byChunk = new Long2ReferenceOpenHashMap<>();
-+ private final Long2ReferenceOpenHashMap<ReferenceList<ServerPlayer>>[] directByChunk = new Long2ReferenceOpenHashMap[TOTAL_MAP_TYPES];
-+ {
-+ for (int i = 0; i < this.directByChunk.length; ++i) {
-+ this.directByChunk[i] = new Long2ReferenceOpenHashMap<>();
-+ }
-+ }
-
- public NearbyPlayers(final ServerLevel world) {
- this.world = world;
-@@ -70,6 +100,16 @@ public final class NearbyPlayers {
- }
- }
-
-+ public void clear() {
-+ if (this.players.isEmpty()) {
-+ return;
-+ }
-+
-+ for (final ServerPlayer player : new ArrayList<>(this.players.keySet())) {
-+ this.removePlayer(player);
-+ }
-+ }
-+
- public void tickPlayer(final ServerPlayer player) {
- final TrackedPlayer[] players = this.players.get(player);
- if (players == null) {
-@@ -94,38 +134,41 @@ public final class NearbyPlayers {
- return this.byChunk.get(CoordinateUtils.getChunkKey(pos));
- }
-
-- public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
-- final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
-+ public TrackedChunk getChunk(final int chunkX, final int chunkZ) {
-+ return this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
-+ }
-
-- return chunk == null ? null : chunk.players[type.ordinal()];
-+ public ReferenceList<ServerPlayer> getPlayers(final BlockPos pos, final NearbyMapType type) {
-+ return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
- }
-
- public ReferenceList<ServerPlayer> getPlayers(final ChunkPos pos, final NearbyMapType type) {
-- final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(pos));
--
-- return chunk == null ? null : chunk.players[type.ordinal()];
-+ return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(pos));
- }
-
- public ReferenceList<ServerPlayer> getPlayersByChunk(final int chunkX, final int chunkZ, final NearbyMapType type) {
-- final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
--
-- return chunk == null ? null : chunk.players[type.ordinal()];
-+ return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
- }
-
- public ReferenceList<ServerPlayer> getPlayersByBlock(final int blockX, final int blockZ, final NearbyMapType type) {
-- final TrackedChunk chunk = this.byChunk.get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
--
-- return chunk == null ? null : chunk.players[type.ordinal()];
-+ return this.directByChunk[type.ordinal()].get(CoordinateUtils.getChunkKey(blockX >> 4, blockZ >> 4));
- }
-
- public static final class TrackedChunk {
-
- private static final ServerPlayer[] EMPTY_PLAYERS_ARRAY = new ServerPlayer[0];
-
-+ private final long chunkKey;
-+ private final NearbyPlayers nearbyPlayers;
- private final ReferenceList<ServerPlayer>[] players = new ReferenceList[TOTAL_MAP_TYPES];
- private int nonEmptyLists;
- private long updateCount;
-
-+ public TrackedChunk(final long chunkKey, final NearbyPlayers nearbyPlayers) {
-+ this.chunkKey = chunkKey;
-+ this.nearbyPlayers = nearbyPlayers;
-+ }
-+
- public boolean isEmpty() {
- return this.nonEmptyLists == 0;
- }
-@@ -145,7 +188,9 @@ public final class NearbyPlayers {
- final ReferenceList<ServerPlayer> list = this.players[idx];
- if (list == null) {
- ++this.nonEmptyLists;
-- (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY)).add(player);
-+ final ReferenceList<ServerPlayer> players = (this.players[idx] = new ReferenceList<>(EMPTY_PLAYERS_ARRAY));
-+ this.nearbyPlayers.directByChunk[idx].put(this.chunkKey, players);
-+ players.add(player);
- return;
- }
-
-@@ -169,6 +214,7 @@ public final class NearbyPlayers {
-
- if (list.size() == 0) {
- this.players[idx] = null;
-+ this.nearbyPlayers.directByChunk[idx].remove(this.chunkKey);
- --this.nonEmptyLists;
- }
- }
-@@ -187,9 +233,19 @@ public final class NearbyPlayers {
- protected void addCallback(final ServerPlayer parameter, final int chunkX, final int chunkZ) {
- final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
-
-- NearbyPlayers.this.byChunk.computeIfAbsent(chunkKey, (final long keyInMap) -> {
-- return new TrackedChunk();
-- }).addPlayer(parameter, this.type);
-+ final TrackedChunk chunk = NearbyPlayers.this.byChunk.get(chunkKey);
-+ final NearbyMapType type = this.type;
-+ if (chunk != null) {
-+ chunk.addPlayer(parameter, type);
-+ type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
-+ } else {
-+ final TrackedChunk created = new TrackedChunk(chunkKey, NearbyPlayers.this);
-+ NearbyPlayers.this.byChunk.put(chunkKey, created);
-+ created.addPlayer(parameter, type);
-+ type.addTo(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
-+
-+ ((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$requestChunkData(chunkKey).nearbyPlayers = created;
-+ }
- }
-
- @Override
-@@ -201,10 +257,16 @@ public final class NearbyPlayers {
- throw new IllegalStateException("Chunk should exist at " + new ChunkPos(chunkKey));
- }
-
-- chunk.removePlayer(parameter, this.type);
-+ final NearbyMapType type = this.type;
-+ chunk.removePlayer(parameter, type);
-+ type.removeFrom(parameter, NearbyPlayers.this.world, chunkX, chunkZ);
-
- if (chunk.isEmpty()) {
- NearbyPlayers.this.byChunk.remove(chunkKey);
-+ final ChunkData chunkData = ((ChunkSystemLevel)NearbyPlayers.this.world).moonrise$releaseChunkData(chunkKey);
-+ if (chunkData != null) {
-+ chunkData.nearbyPlayers = null;
-+ }
- }
- }
- }
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 96bbab283eb6c7e7863383fea0ddc62510391091..fc029c8fb22a7c8eeb23bfc171812f6da91c60fa 100644
+index b61611351bf23efc1e90bab8a850ebbe6ffdd516..fc029c8fb22a7c8eeb23bfc171812f6da91c60fa 100644
--- a/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/ChunkSystem.java
-@@ -1,15 +1,18 @@
- package ca.spottedleaf.moonrise.common.util;
-
- import ca.spottedleaf.concurrentutil.util.Priority;
-+import ca.spottedleaf.moonrise.common.PlatformHooks;
- import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
+@@ -6,11 +6,13 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevelChunk;
import ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader;
import ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemServerChunkCache;
@@ -707,17 +22,7 @@ index 96bbab283eb6c7e7863383fea0ddc62510391091..fc029c8fb22a7c8eeb23bfc171812f6d
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
-@@ -68,6 +71,9 @@ public final class ChunkSystem {
- }
-
- public static boolean screenEntity(final ServerLevel level, final Entity entity, final boolean fromDisk, final boolean event) {
-+ if (!PlatformHooks.get().screenEntity(level, entity, fromDisk, event)) {
-+ return false;
-+ }
- return true;
- }
-
-@@ -76,7 +82,13 @@ public final class ChunkSystem {
+@@ -80,7 +82,13 @@ public final class ChunkSystem {
}
public static void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
@@ -732,7 +37,7 @@ index 96bbab283eb6c7e7863383fea0ddc62510391091..fc029c8fb22a7c8eeb23bfc171812f6d
}
public static void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) {
-@@ -108,16 +120,18 @@ public final class ChunkSystem {
+@@ -112,16 +120,18 @@ public final class ChunkSystem {
((ChunkSystemLevelChunk)chunk).moonrise$getChunkAndHolder()
);
if (!((ChunkSystemLevelChunk)chunk).moonrise$isPostProcessingDone()) {
@@ -752,266 +57,6 @@ index 96bbab283eb6c7e7863383fea0ddc62510391091..fc029c8fb22a7c8eeb23bfc171812f6d
}
public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MixinWorkarounds.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MixinWorkarounds.java
-index ac6f284ee4469d16c5655328b2488d7612832353..97848869df61648fc415e4d39f409f433202c274 100644
---- a/src/main/java/ca/spottedleaf/moonrise/common/util/MixinWorkarounds.java
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MixinWorkarounds.java
-@@ -3,8 +3,12 @@ package ca.spottedleaf.moonrise.common.util;
- public final class MixinWorkarounds {
-
- // mixins tries to find the owner of the clone() method, which doesn't exist and NPEs
-+ // https://github.com/FabricMC/Mixin/pull/147
- public static long[] clone(final long[] values) {
- return values.clone();
- }
-
-+ public static byte[] clone(final byte[] values) {
-+ return values.clone();
-+ }
- }
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
-index 3abe0bd2a820352b85306d554bf14a4cf6123091..c125c70a68130be373acc989053a6c0e487be924 100644
---- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseCommon.java
-@@ -1,45 +1,100 @@
- package ca.spottedleaf.moonrise.common.util;
-
--import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadPool;
-+import ca.spottedleaf.concurrentutil.executor.thread.PrioritisedThreadPool;
-+import ca.spottedleaf.moonrise.common.PlatformHooks;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
--import java.io.File;
-+import java.util.concurrent.TimeUnit;
-+import java.util.concurrent.atomic.AtomicInteger;
-+import java.util.function.Consumer;
-
- public final class MoonriseCommon {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(MoonriseCommon.class);
-
-- // Paper start
-- public static PrioritisedThreadPool WORKER_POOL;
-- public static int WORKER_THREADS;
-- public static void init(io.papermc.paper.configuration.GlobalConfiguration.ChunkSystem chunkSystem) {
-- // Paper end
-+ public static final PrioritisedThreadPool WORKER_POOL = new PrioritisedThreadPool(
-+ new Consumer<>() {
-+ private final AtomicInteger idGenerator = new AtomicInteger();
-+
-+ @Override
-+ public void accept(Thread thread) {
-+ thread.setDaemon(true);
-+ thread.setName(PlatformHooks.get().getBrand() + " Common Worker #" + this.idGenerator.getAndIncrement());
-+ thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
-+ @Override
-+ public void uncaughtException(final Thread thread, final Throwable throwable) {
-+ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
-+ }
-+ });
-+ }
-+ }
-+ );
-+ public static final long WORKER_QUEUE_HOLD_TIME = (long)(20.0e6); // 20ms
-+ public static final int CLIENT_DIVISION = 0;
-+ public static final PrioritisedThreadPool.ExecutorGroup RENDER_EXECUTOR_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(CLIENT_DIVISION, 0);
-+ public static final int SERVER_DIVISION = 1;
-+ public static final PrioritisedThreadPool.ExecutorGroup PARALLEL_GEN_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
-+ public static final PrioritisedThreadPool.ExecutorGroup RADIUS_AWARE_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
-+ public static final PrioritisedThreadPool.ExecutorGroup LOAD_GROUP = MoonriseCommon.WORKER_POOL.createExecutorGroup(SERVER_DIVISION, 0);
-+
-+ public static void adjustWorkerThreads(final int configWorkerThreads, final int configIoThreads) {
- int defaultWorkerThreads = Runtime.getRuntime().availableProcessors() / 2;
- if (defaultWorkerThreads <= 4) {
- defaultWorkerThreads = defaultWorkerThreads <= 3 ? 1 : 2;
- } else {
- defaultWorkerThreads = defaultWorkerThreads / 2;
- }
-- defaultWorkerThreads = Integer.getInteger("Paper.WorkerThreadCount", Integer.valueOf(defaultWorkerThreads)); // Paper
-+ defaultWorkerThreads = Integer.getInteger(PlatformHooks.get().getBrand() + ".WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
-
-- int workerThreads = chunkSystem.workerThreads; // Paper
-+ int workerThreads = configWorkerThreads;
-
- if (workerThreads <= 0) {
- workerThreads = defaultWorkerThreads;
- }
-
-- WORKER_POOL = new PrioritisedThreadPool(
-- "Paper Worker Pool", workerThreads, // Paper
-- (final Thread thread, final Integer id) -> {
-- thread.setName("Paper Common Worker #" + id.intValue()); // Paper
-+ final int ioThreads = Math.max(1, configIoThreads);
-+
-+ WORKER_POOL.adjustThreadCount(workerThreads);
-+ IO_POOL.adjustThreadCount(ioThreads);
-+
-+ LOGGER.info(PlatformHooks.get().getBrand() + " is using " + workerThreads + " worker threads, " + ioThreads + " I/O threads");
-+ }
-+
-+ public static final PrioritisedThreadPool IO_POOL = new PrioritisedThreadPool(
-+ new Consumer<>() {
-+ private final AtomicInteger idGenerator = new AtomicInteger();
-+
-+ @Override
-+ public void accept(final Thread thread) {
-+ thread.setDaemon(true);
-+ thread.setName(PlatformHooks.get().getBrand() + " I/O Worker #" + this.idGenerator.getAndIncrement());
- thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(final Thread thread, final Throwable throwable) {
- LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
- }
- });
-- }, (long)(20.0e6)); // 20ms
-- WORKER_THREADS = workerThreads;
-+ }
-+ }
-+ );
-+ public static final long IO_QUEUE_HOLD_TIME = (long)(100.0e6); // 100ms
-+ public static final PrioritisedThreadPool.ExecutorGroup CLIENT_PROFILER_IO_GROUP = IO_POOL.createExecutorGroup(CLIENT_DIVISION, 0);
-+ public static final PrioritisedThreadPool.ExecutorGroup SERVER_REGION_IO_GROUP = IO_POOL.createExecutorGroup(SERVER_DIVISION, 0);
-+
-+ public static void haltExecutors() {
-+ MoonriseCommon.WORKER_POOL.shutdown(false);
-+ LOGGER.info("Awaiting termination of worker pool for up to 60s...");
-+ if (!MoonriseCommon.WORKER_POOL.join(TimeUnit.SECONDS.toMillis(60L))) {
-+ LOGGER.error("Worker pool did not shut down in time!");
-+ MoonriseCommon.WORKER_POOL.halt(false);
-+ }
-+
-+ MoonriseCommon.IO_POOL.shutdown(false);
-+ LOGGER.info("Awaiting termination of I/O pool for up to 60s...");
-+ if (!MoonriseCommon.IO_POOL.join(TimeUnit.SECONDS.toMillis(60L))) {
-+ LOGGER.error("I/O pool did not shut down in time!");
-+ MoonriseCommon.IO_POOL.halt(false);
-+ }
- }
-
- private MoonriseCommon() {}
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
-index 1cf32d7d1bbc8a0a3f7cb9024c793f6744199f64..559c959aff3c9deef867b9e425fba3e2e669cac6 100644
---- a/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/MoonriseConstants.java
-@@ -1,8 +1,10 @@
- package ca.spottedleaf.moonrise.common.util;
-
-+import ca.spottedleaf.moonrise.common.PlatformHooks;
-+
- public final class MoonriseConstants {
-
-- public static final int MAX_VIEW_DISTANCE = 32;
-+ public static final int MAX_VIEW_DISTANCE = Integer.getInteger(PlatformHooks.get().getBrand() + ".MaxViewDistance", 32);
-
- private MoonriseConstants() {}
-
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java b/src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..a9ff1c1a70faf4b7a64b265932f07a8b8f00c1ff
---- /dev/null
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java
-@@ -0,0 +1,52 @@
-+package ca.spottedleaf.moonrise.common.util;
-+
-+import net.minecraft.world.level.levelgen.LegacyRandomSource;
-+
-+/**
-+ * Avoid costly CAS of superclass
-+ */
-+public final class SimpleRandom extends LegacyRandomSource {
-+
-+ private static final long MULTIPLIER = 25214903917L;
-+ private static final long ADDEND = 11L;
-+ private static final int BITS = 48;
-+ private static final long MASK = (1L << BITS) - 1;
-+
-+ private long value;
-+
-+ public SimpleRandom(final long seed) {
-+ super(0L);
-+ this.value = seed;
-+ }
-+
-+ @Override
-+ public void setSeed(final long seed) {
-+ this.value = (seed ^ MULTIPLIER) & MASK;
-+ }
-+
-+ private long advanceSeed() {
-+ return this.value = ((this.value * MULTIPLIER) + ADDEND) & MASK;
-+ }
-+
-+ @Override
-+ public int next(final int bits) {
-+ return (int)(this.advanceSeed() >>> (BITS - bits));
-+ }
-+
-+ @Override
-+ public int nextInt() {
-+ final long seed = this.advanceSeed();
-+ return (int)(seed >>> (BITS - Integer.SIZE));
-+ }
-+
-+ @Override
-+ public int nextInt(final int bound) {
-+ if (bound <= 0) {
-+ throw new IllegalArgumentException();
-+ }
-+
-+ // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
-+ final long value = this.advanceSeed() >>> (BITS - Integer.SIZE);
-+ return (int)((value * (long)bound) >>> Integer.SIZE);
-+ }
-+}
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
-index 11b7f15755dde766140c29bedca456c80d53293f..217d1f908a36a5177ba3cbb80a33f73d4dab0fa0 100644
---- a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
-@@ -77,11 +77,15 @@ public class TickThread extends Thread {
- }
-
- public TickThread(final Runnable run, final String name) {
-- this(run, name, ID_GENERATOR.incrementAndGet());
-+ this(null, run, name);
- }
-
-- private TickThread(final Runnable run, final String name, final int id) {
-- super(run, name);
-+ public TickThread(final ThreadGroup group, final Runnable run, final String name) {
-+ this(group, run, name, ID_GENERATOR.incrementAndGet());
-+ }
-+
-+ private TickThread(final ThreadGroup group, final Runnable run, final String name, final int id) {
-+ super(group, run, name);
- this.id = id;
- }
-
-diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/WorldUtil.java b/src/main/java/ca/spottedleaf/moonrise/common/util/WorldUtil.java
-index af9623240ff2d389aa7090623f507720e7dbab7d..efda2688ae1254a82ba7f6bf8bf597ef224cbb86 100644
---- a/src/main/java/ca/spottedleaf/moonrise/common/util/WorldUtil.java
-+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/WorldUtil.java
-@@ -8,11 +8,19 @@ public final class WorldUtil {
- // min, max are inclusive
-
- public static int getMaxSection(final LevelHeightAccessor world) {
-- return world.getMaxSection() - 1; // getMaxSection() is exclusive
-+ return world.getMaxSectionY();
-+ }
-+
-+ public static int getMaxSection(final Level world) {
-+ return world.getMaxSectionY();
- }
-
- public static int getMinSection(final LevelHeightAccessor world) {
-- return world.getMinSection();
-+ return world.getMinSectionY();
-+ }
-+
-+ public static int getMinSection(final Level world) {
-+ return world.getMinSectionY();
- }
-
- public static int getMaxLightSection(final LevelHeightAccessor world) {
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingBitStorage.java b/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingBitStorage.java
index aef4fc0d3c272febe675d1ac846b88e58b4e7533..93bc56daec4526f373c84763b8c7ccb4a30e800b 100644
--- a/src/main/java/ca/spottedleaf/moonrise/patches/block_counting/BlockCountingBitStorage.java