Migrate ChunkSystem class to PaperHooks

This commit is contained in:
Spottedleaf
2024-12-16 10:42:50 -08:00
parent 0ed399bb41
commit 6186079231
13 changed files with 897 additions and 510 deletions

View File

@@ -1,5 +1,6 @@
package ca.spottedleaf.moonrise.common;
import ca.spottedleaf.moonrise.common.util.ChunkSystemHooks;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import net.minecraft.core.BlockPos;
@@ -23,7 +24,7 @@ import java.util.List;
import java.util.ServiceLoader;
import java.util.function.Predicate;
public interface PlatformHooks {
public interface PlatformHooks extends ChunkSystemHooks {
public static PlatformHooks get() {
return Holder.INSTANCE;
}
@@ -63,8 +64,6 @@ public interface PlatformHooks {
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();

View File

@@ -1,288 +0,0 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.concurrentutil.util.Priority;
import ca.spottedleaf.moonrise.common.PlatformHooks;
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.world.entity.Entity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import org.slf4j.Logger;
import java.util.List;
import java.util.function.Consumer;
public final class ChunkSystem {
private static final Logger LOGGER = LogUtils.getLogger();
private static final net.minecraft.world.level.chunk.status.ChunkStep FULL_CHUNK_STEP = net.minecraft.world.level.chunk.status.ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FULL);
private static int getDistance(final ChunkStatus status) {
return FULL_CHUNK_STEP.getAccumulatedRadiusOf(status);
}
public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
scheduleChunkTask(level, chunkX, chunkZ, run, Priority.NORMAL);
}
public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority) {
level.chunkSource.mainThreadProcessor.execute(run);
}
public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
final ChunkStatus toStatus, final boolean addTicket, final Priority priority,
final Consumer<ChunkAccess> onComplete) {
if (gen) {
scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
return;
}
scheduleChunkLoad(level, chunkX, chunkZ, ChunkStatus.EMPTY, addTicket, priority, (final ChunkAccess chunk) -> {
if (chunk == null) {
if (onComplete != null) {
onComplete.accept(null);
}
} else {
if (chunk.getPersistedStatus().isOrAfter(toStatus)) {
scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
} else {
if (onComplete != null) {
onComplete.accept(null);
}
}
}
});
}
static final net.minecraft.server.level.TicketType<Long> CHUNK_LOAD = net.minecraft.server.level.TicketType.create("chunk_load", Long::compareTo);
private static long chunkLoadCounter = 0L;
public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) {
if (!org.bukkit.Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
scheduleChunkTask(level, chunkX, chunkZ, () -> {
scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
}, priority);
return;
}
final int minLevel = 33 + getDistance(toStatus);
final Long chunkReference = addTicket ? Long.valueOf(++chunkLoadCounter) : null;
final net.minecraft.world.level.ChunkPos chunkPos = new net.minecraft.world.level.ChunkPos(chunkX, chunkZ);
if (addTicket) {
level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
}
level.chunkSource.runDistanceManagerUpdates();
final Consumer<ChunkAccess> loadCallback = (final ChunkAccess chunk) -> {
try {
if (onComplete != null) {
onComplete.accept(chunk);
}
} catch (final Throwable thr) {
LOGGER.error("Exception handling chunk load callback", thr);
com.destroystokyo.paper.util.SneakyThrow.sneaky(thr);
} finally {
if (addTicket) {
level.chunkSource.addTicketAtLevel(net.minecraft.server.level.TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
}
}
};
final ChunkHolder holder = level.chunkSource.chunkMap.updatingChunkMap.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
if (holder == null || holder.getTicketLevel() > minLevel) {
loadCallback.accept(null);
return;
}
final java.util.concurrent.CompletableFuture<net.minecraft.server.level.ChunkResult<net.minecraft.world.level.chunk.ChunkAccess>> loadFuture = holder.scheduleChunkGenerationTask(toStatus, level.chunkSource.chunkMap);
if (loadFuture.isDone()) {
loadCallback.accept(loadFuture.join().orElse(null));
return;
}
loadFuture.whenCompleteAsync((final net.minecraft.server.level.ChunkResult<net.minecraft.world.level.chunk.ChunkAccess> result, final Throwable thr) -> {
if (thr != null) {
loadCallback.accept(null);
return;
}
loadCallback.accept(result.orElse(null));
}, (final Runnable r) -> {
scheduleChunkTask(level, chunkX, chunkZ, r, Priority.HIGHEST);
});
}
public static void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
final FullChunkStatus toStatus, final boolean addTicket,
final Priority priority, final Consumer<LevelChunk> onComplete) {
// This method goes unused until the chunk system rewrite
if (toStatus == FullChunkStatus.INACCESSIBLE) {
throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status");
}
if (!org.bukkit.Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
scheduleChunkTask(level, chunkX, chunkZ, () -> {
scheduleTickingState(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
}, priority);
return;
}
final int minLevel = 33 - (toStatus.ordinal() - 1);
final int radius = toStatus.ordinal() - 1;
final Long chunkReference = addTicket ? Long.valueOf(++chunkLoadCounter) : null;
final net.minecraft.world.level.ChunkPos chunkPos = new net.minecraft.world.level.ChunkPos(chunkX, chunkZ);
if (addTicket) {
level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
}
level.chunkSource.runDistanceManagerUpdates();
final Consumer<LevelChunk> loadCallback = (final LevelChunk chunk) -> {
try {
if (onComplete != null) {
onComplete.accept(chunk);
}
} catch (final Throwable thr) {
LOGGER.error("Exception handling chunk load callback", thr);
com.destroystokyo.paper.util.SneakyThrow.sneaky(thr);
} finally {
if (addTicket) {
level.chunkSource.addTicketAtLevel(net.minecraft.server.level.TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
}
}
};
final ChunkHolder holder = level.chunkSource.chunkMap.updatingChunkMap.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
if (holder == null || holder.getTicketLevel() > minLevel) {
loadCallback.accept(null);
return;
}
final java.util.concurrent.CompletableFuture<net.minecraft.server.level.ChunkResult<net.minecraft.world.level.chunk.LevelChunk>> tickingState;
switch (toStatus) {
case FULL: {
tickingState = holder.getFullChunkFuture();
break;
}
case BLOCK_TICKING: {
tickingState = holder.getTickingChunkFuture();
break;
}
case ENTITY_TICKING: {
tickingState = holder.getEntityTickingChunkFuture();
break;
}
default: {
throw new IllegalStateException("Cannot reach here");
}
}
if (tickingState.isDone()) {
loadCallback.accept(tickingState.join().orElse(null));
return;
}
tickingState.whenCompleteAsync((final net.minecraft.server.level.ChunkResult<net.minecraft.world.level.chunk.LevelChunk> result, final Throwable thr) -> {
if (thr != null) {
loadCallback.accept(null);
return;
}
loadCallback.accept(result.orElse(null));
}, (final Runnable r) -> {
scheduleChunkTask(level, chunkX, chunkZ, r, Priority.HIGHEST);
});
}
public static List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
return new java.util.ArrayList<>(level.chunkSource.chunkMap.visibleChunkMap.values());
}
public static List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
return new java.util.ArrayList<>(level.chunkSource.chunkMap.updatingChunkMap.values());
}
public static int getVisibleChunkHolderCount(final ServerLevel level) {
return level.chunkSource.chunkMap.visibleChunkMap.size();
}
public static int getUpdatingChunkHolderCount(final ServerLevel level) {
return level.chunkSource.chunkMap.updatingChunkMap.size();
}
public static boolean hasAnyChunkHolders(final ServerLevel level) {
return getUpdatingChunkHolderCount(level) != 0;
}
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;
}
public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
}
public static void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
}
public static void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
}
public static void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
}
public static void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) {
}
public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
}
public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
}
public static void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
}
public static ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) {
return level.chunkSource.chunkMap.getUnloadingChunkHolder(chunkX, chunkZ);
}
public static int getSendViewDistance(final ServerPlayer player) {
return getViewDistance(player);
}
public static int getViewDistance(final ServerPlayer player) {
final ServerLevel level = player.serverLevel();
if (level == null) {
return org.bukkit.Bukkit.getViewDistance();
}
return level.chunkSource.chunkMap.serverViewDistance;
}
public static int getTickViewDistance(final ServerPlayer player) {
final ServerLevel level = player.serverLevel();
if (level == null) {
return org.bukkit.Bukkit.getSimulationDistance();
}
return level.chunkSource.chunkMap.distanceManager.simulationDistance;
}
private ChunkSystem() {}
}

View File

@@ -0,0 +1,77 @@
package ca.spottedleaf.moonrise.common.util;
import ca.spottedleaf.concurrentutil.util.Priority;
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.world.entity.Entity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import java.util.List;
import java.util.function.Consumer;
public interface ChunkSystemHooks {
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run);
public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority);
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
final ChunkStatus toStatus, final boolean addTicket, final Priority priority,
final Consumer<ChunkAccess> onComplete);
public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete);
public void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
final FullChunkStatus toStatus, final boolean addTicket,
final Priority priority, final Consumer<LevelChunk> onComplete);
public List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level);
public List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level);
public int getVisibleChunkHolderCount(final ServerLevel level);
public int getUpdatingChunkHolderCount(final ServerLevel level);
public boolean hasAnyChunkHolders(final ServerLevel level);
public boolean screenEntity(final ServerLevel level, final Entity entity, final boolean fromDisk, final boolean event);
public void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder);
public void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder);
public void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder);
public void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder);
public ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ);
public int getSendViewDistance(final ServerPlayer player);
public int getViewDistance(final ServerPlayer player);
public int getTickViewDistance(final ServerPlayer player);
public void addPlayerToDistanceMaps(final ServerLevel world, final ServerPlayer player);
public void removePlayerFromDistanceMaps(final ServerLevel world, final ServerPlayer player);
public void updateMaps(final ServerLevel world, final ServerPlayer player);
}

View File

@@ -9,7 +9,7 @@ import net.minecraft.world.level.levelgen.PositionalRandomFactory;
/**
* Avoid costly CAS of superclass
*/
public final class ThreadUnsafeRandom implements BitRandomSource {
public class ThreadUnsafeRandom implements BitRandomSource { // Paper - replace random
private static final long MULTIPLIER = 25214903917L;
private static final long ADDEND = 11L;

View File

@@ -216,7 +216,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
public int getTileEntityCount() {
// We don't use the full world tile entity list, so we must iterate chunks
int size = 0;
for (ChunkHolder playerchunk : ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.world)) {
for (ChunkHolder playerchunk : ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.world)) {
net.minecraft.world.level.chunk.LevelChunk chunk = playerchunk.getTickingChunk();
if (chunk == null) {
continue;
@@ -405,7 +405,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return chunk instanceof ImposterProtoChunk || chunk instanceof net.minecraft.world.level.chunk.LevelChunk;
}
final java.util.concurrent.CompletableFuture<ChunkAccess> future = new java.util.concurrent.CompletableFuture<>();
ca.spottedleaf.moonrise.common.util.ChunkSystem.scheduleChunkLoad(
ca.spottedleaf.moonrise.common.PlatformHooks.get().scheduleChunkLoad(
this.world, x, z, false, ChunkStatus.EMPTY, true, ca.spottedleaf.concurrentutil.util.Priority.NORMAL, future::complete
);
world.getChunkSource().mainThreadProcessor.managedBlock(future::isDone);
@@ -420,7 +420,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public Chunk[] getLoadedChunks() {
List<ChunkHolder> chunks = ca.spottedleaf.moonrise.common.util.ChunkSystem.getVisibleChunkHolders(this.world); // Paper
List<ChunkHolder> chunks = ca.spottedleaf.moonrise.common.PlatformHooks.get().getVisibleChunkHolders(this.world); // Paper
return chunks.stream().map(ChunkHolder::getFullChunkNow).filter(Objects::nonNull).map(CraftChunk::new).toArray(Chunk[]::new);
}
@@ -2447,7 +2447,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void getChunkAtAsync(int x, int z, boolean gen, boolean urgent, @NotNull Consumer<? super Chunk> cb) {
warnUnsafeChunk("getting a faraway chunk async", x, z); // Paper
ca.spottedleaf.moonrise.common.util.ChunkSystem.scheduleChunkLoad(
ca.spottedleaf.moonrise.common.PlatformHooks.get().scheduleChunkLoad(
this.getHandle(), x, z, gen, ChunkStatus.FULL, true,
urgent ? ca.spottedleaf.concurrentutil.util.Priority.HIGHER : ca.spottedleaf.concurrentutil.util.Priority.NORMAL,
(ChunkAccess chunk) -> {

View File

@@ -3522,7 +3522,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public int getViewDistance() {
return ca.spottedleaf.moonrise.common.util.ChunkSystem.getViewDistance(this.getHandle());
return ca.spottedleaf.moonrise.common.PlatformHooks.get().getViewDistance(this.getHandle());
}
@Override
@@ -3532,7 +3532,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public int getSimulationDistance() {
return ca.spottedleaf.moonrise.common.util.ChunkSystem.getTickViewDistance(this.getHandle());
return ca.spottedleaf.moonrise.common.PlatformHooks.get().getTickViewDistance(this.getHandle());
}
@Override
@@ -3542,7 +3542,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public int getSendViewDistance() {
return ca.spottedleaf.moonrise.common.util.ChunkSystem.getSendViewDistance(this.getHandle());
return ca.spottedleaf.moonrise.common.PlatformHooks.get().getSendViewDistance(this.getHandle());
}
@Override