Add relight command

This is simply for feature parity with Paper.
This commit is contained in:
Spottedleaf
2024-06-18 15:22:14 -07:00
parent 240faf56a4
commit ac971bee98
7 changed files with 217 additions and 3 deletions

View File

@@ -1,12 +1,21 @@
package ca.spottedleaf.moonrise.mixin.starlight.lightengine;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemChunkHolder;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import ca.spottedleaf.moonrise.patches.starlight.light.StarLightInterface;
import ca.spottedleaf.moonrise.patches.starlight.light.StarLightLightingProvider;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.util.thread.ProcessorHandle;
import net.minecraft.util.thread.ProcessorMailbox;
import net.minecraft.world.level.ChunkPos;
@@ -24,8 +33,17 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
@@ -78,6 +96,85 @@ public abstract class ThreadedLevelLightEngineMixin extends LevelLightEngine imp
});
}
@Override
public final int starlight$serverRelightChunks(final Collection<ChunkPos> chunks0,
final Consumer<ChunkPos> chunkLightCallback,
final IntConsumer onComplete) {
final Set<ChunkPos> chunks = new LinkedHashSet<>(chunks0);
final Map<ChunkPos, Long> ticketIds = new HashMap<>();
final ServerLevel world = (ServerLevel)this.starlight$getLightEngine().getWorld();
for (final Iterator<ChunkPos> iterator = chunks.iterator(); iterator.hasNext();) {
final ChunkPos pos = iterator.next();
final Long id = ChunkTaskScheduler.getNextChunkRelightId();
world.getChunkSource().addRegionTicket(ChunkTaskScheduler.CHUNK_RELIGHT, pos, StarLightInterface.REGION_LIGHT_TICKET_LEVEL, id);
ticketIds.put(pos, id);
final ChunkAccess chunk = (ChunkAccess)world.getChunkSource().getChunkForLighting(pos.x, pos.z);
if (chunk == null || !chunk.isLightCorrect() || !chunk.getPersistedStatus().isOrAfter(ChunkStatus.LIGHT)) {
// cannot relight this chunk
iterator.remove();
ticketIds.remove(pos);
world.getChunkSource().removeRegionTicket(ChunkTaskScheduler.CHUNK_RELIGHT, pos, StarLightInterface.REGION_LIGHT_TICKET_LEVEL, id);
continue;
}
}
((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().radiusAwareScheduler.queueInfiniteRadiusTask(() -> {
ThreadedLevelLightEngineMixin.this.starlight$getLightEngine().relightChunks(
chunks,
(final ChunkPos pos) -> {
if (chunkLightCallback != null) {
chunkLightCallback.accept(pos);
}
((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().scheduleChunkTask(pos.x, pos.z, () -> {
final NewChunkHolder chunkHolder = ((ChunkSystemServerLevel)world).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(
pos.x, pos.z
);
if (chunkHolder == null) {
return;
}
final List<ServerPlayer> players = ((ChunkSystemChunkHolder)chunkHolder.vanillaChunkHolder).moonrise$getPlayers(false);
if (players.isEmpty()) {
return;
}
final Packet<?> relightPacket = new ClientboundLightUpdatePacket(
pos, (ThreadedLevelLightEngine)(Object)ThreadedLevelLightEngineMixin.this,
null, null
);
for (final ServerPlayer player : players) {
final ServerGamePacketListenerImpl conn = player.connection;
if (conn != null) {
conn.send(relightPacket);
}
}
});
},
(final int relight) -> {
if (onComplete != null) {
onComplete.accept(relight);
}
for (final Map.Entry<ChunkPos, Long> entry : ticketIds.entrySet()) {
world.getChunkSource().removeRegionTicket(
ChunkTaskScheduler.CHUNK_RELIGHT, entry.getKey(),
StarLightInterface.REGION_LIGHT_TICKET_LEVEL, entry.getValue()
);
}
}
);
});
return chunks.size();
}
/**
* @reason Destroy old chunk system hook
* @author Spottedleaf

View File

@@ -14,6 +14,7 @@ import ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemLevel
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler;
import ca.spottedleaf.moonrise.patches.chunk_system.util.ParallelSearchRadiusIteration;
import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
@@ -58,7 +59,7 @@ public final class RegionizedPlayerChunkLoader {
public static final int LOADED_TICKET_LEVEL = ChunkTaskScheduler.getTicketLevel(ChunkStatus.EMPTY);
public static final int TICK_TICKET_LEVEL = ChunkHolderManager.ENTITY_TICKING_TICKET_LEVEL;
public static class ViewDistanceHolder {
public static final class ViewDistanceHolder {
private volatile ViewDistances viewDistances;
private static final VarHandle VIEW_DISTANCES_HANDLE = ConcurrentUtil.getVarHandle(ViewDistanceHolder.class, "viewDistances", ViewDistances.class);
@@ -106,6 +107,10 @@ public final class RegionizedPlayerChunkLoader {
return param.setTickViewDistance(distance);
});
}
public JsonObject toJson() {
return this.getViewDistances().toJson();
}
}
public static final record ViewDistances(
@@ -124,6 +129,16 @@ public final class RegionizedPlayerChunkLoader {
public ViewDistances setSendViewDistance(final int distance) {
return new ViewDistances(this.tickViewDistance, this.loadViewDistance, distance);
}
public JsonObject toJson() {
final JsonObject ret = new JsonObject();
ret.addProperty("tick-view-distance", this.tickViewDistance);
ret.addProperty("load-view-distance", this.loadViewDistance);
ret.addProperty("send-view-distance", this.sendViewDistance);
return ret;
}
}
public static int getAPITickViewDistance(final ServerPlayer player) {

View File

@@ -1282,7 +1282,6 @@ public final class ChunkHolderManager {
}
private boolean processTicketUpdates(final boolean processFullUpdates, List<ChunkProgressionTask> scheduledTasks) {
TickThread.ensureTickThread("Cannot process ticket levels off-main");
if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager");
}

View File

@@ -112,6 +112,13 @@ public final class ChunkTaskScheduler {
return Long.valueOf(POI_LOAD_IDS.getAndIncrement());
}
public static final TicketType<Long> CHUNK_RELIGHT = TicketType.create("starlight:chunk_relight", Long::compareTo);
private static final AtomicLong CHUNK_RELIGHT_IDS = new AtomicLong();
public static Long getNextChunkRelightId() {
return Long.valueOf(CHUNK_RELIGHT_IDS.getAndIncrement());
}
public static int getTicketLevel(final ChunkStatus status) {
return ChunkLevel.byStatus(status);

View File

@@ -9,7 +9,7 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import java.util.Arrays;
import java.util.Objects;
public class ParallelSearchRadiusIteration {
public final class ParallelSearchRadiusIteration {
// expected that this list returns for a given radius, the set of chunks ordered
// by manhattan distance

View File

@@ -1,21 +1,34 @@
package ca.spottedleaf.moonrise.patches.command;
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
import ca.spottedleaf.moonrise.common.util.MoonriseCommon;
import ca.spottedleaf.moonrise.common.util.MoonriseConstants;
import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel;
import ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer;
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
import ca.spottedleaf.moonrise.patches.starlight.light.StarLightLightingProvider;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.context.CommandContext;
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.contents.PlainTextContents;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.phys.Vec3;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public final class MoonriseCommand {
@@ -38,6 +51,17 @@ public final class MoonriseCommand {
.executes((final CommandContext<CommandSourceStack> ctx) -> {
return MoonriseCommand.reload(ctx);
})
).then(
Commands.literal("relight")
.executes((final CommandContext<CommandSourceStack> ctx) -> {
return MoonriseCommand.relight(ctx, 10);
})
.then(
Commands.argument("radius", IntegerArgumentType.integer(0, MoonriseConstants.MAX_VIEW_DISTANCE))
.executes((final CommandContext<CommandSourceStack> ctx) -> {
return MoonriseCommand.relight(ctx, IntegerArgumentType.getInteger(ctx, "radius"));
})
)
)
);
}
@@ -151,4 +175,67 @@ public final class MoonriseCommand {
return 0;
}
public static int relight(final CommandContext<CommandSourceStack> ctx, final int radius) {
final Vec3 center = ctx.getSource().getPosition();
final int centerChunkX = Mth.floor(center.x) >> 4;
final int centerChunkZ = Mth.floor(center.z) >> 4;
final List<ChunkPos> chunks = new ArrayList<>();
final LongOpenHashSet seen = new LongOpenHashSet();
final LongArrayFIFOQueue queue = new LongArrayFIFOQueue();
final long zero = CoordinateUtils.getChunkKey(0, 0);
seen.add(zero);
queue.enqueue(zero);
chunks.add(new ChunkPos(centerChunkX, centerChunkZ));
final int[][] offsets = new int[][] {
new int[] { -1, 0 },
new int[] { 1, 0 },
new int[] { 0, -1 },
new int[] { 0, 1 }
};
while (!queue.isEmpty()) {
final long chunk = queue.dequeueLong();
final int chunkX = CoordinateUtils.getChunkX(chunk);
final int chunkZ = CoordinateUtils.getChunkZ(chunk);
for (final int[] offset : offsets) {
final int neighbourX = chunkX + offset[0];
final int neighbourZ = chunkZ + offset[1];
final long neighbour = CoordinateUtils.getChunkKey(neighbourX, neighbourZ);
final int dist = Math.max(Math.abs(neighbourX), Math.abs(neighbourZ));
if (dist > radius || !seen.add(neighbour)) {
continue;
}
queue.enqueue(neighbour);
chunks.add(new ChunkPos(neighbourX + centerChunkX, neighbourZ + centerChunkZ));
}
}
final int ret = ((StarLightLightingProvider)ctx.getSource().getLevel().getLightEngine()).starlight$serverRelightChunks(
chunks,
null,
null
);
ctx.getSource().sendSuccess(() -> {
return MutableComponent.create(
new PlainTextContents.LiteralContents(
"Relighting " + ret + " chunks"
)
);
}, true);
return ret;
}
}

View File

@@ -5,6 +5,9 @@ import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.DataLayer;
import net.minecraft.world.level.chunk.LevelChunk;
import java.util.Collection;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
public interface StarLightLightingProvider {
@@ -17,4 +20,10 @@ public interface StarLightLightingProvider {
public void starlight$clientChunkLoad(final ChunkPos pos, final LevelChunk chunk);
public default int starlight$serverRelightChunks(final Collection<ChunkPos> chunks,
final Consumer<ChunkPos> chunkLightCallback,
final IntConsumer onComplete) throws UnsupportedOperationException {
throw new UnsupportedOperationException();
}
}