Add relight command
This is simply for feature parity with Paper.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user