hollow-cube/lighting-update-2

Delay light sending, send lighting slower, fix sending multiple times

---------

Co-authored-by: mworzala <mattheworzala@gmail.com>
This commit is contained in:
iam 2023-06-18 15:16:09 -04:00 committed by GitHub
parent 438338381e
commit 8a5c610b7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 46 deletions

View File

@ -120,6 +120,8 @@ public class PlayerInit {
var itemStack = event.getItemStack(); var itemStack = event.getItemStack();
var block = event.getInstance().getBlock(event.getPosition()); var block = event.getInstance().getBlock(event.getPosition());
event.getPlayer().sendMessage("MESSAGE " + ThreadLocalRandom.current().nextDouble());
if ("false".equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.WATER_BUCKET)) { if ("false".equals(block.getProperty("waterlogged")) && itemStack.material().equals(Material.WATER_BUCKET)) {
block = block.withProperty("waterlogged", "true"); block = block.withProperty("waterlogged", "true");
System.out.println("SET WATERLOGGER"); System.out.println("SET WATERLOGGER");
@ -139,14 +141,14 @@ public class PlayerInit {
instanceContainer.setGenerator(unit -> unit.modifier().fillHeight(0, 40, Block.STONE)); instanceContainer.setGenerator(unit -> unit.modifier().fillHeight(0, 40, Block.STONE));
instanceContainer.setChunkSupplier(LightingChunk::new); instanceContainer.setChunkSupplier(LightingChunk::new);
System.out.println("start"); // System.out.println("start");
var chunks = new ArrayList<CompletableFuture<Chunk>>(); // var chunks = new ArrayList<CompletableFuture<Chunk>>();
ChunkUtils.forChunksInRange(0, 0, 32, (x, z) -> chunks.add(instanceContainer.loadChunk(x, z))); // ChunkUtils.forChunksInRange(0, 0, 32, (x, z) -> chunks.add(instanceContainer.loadChunk(x, z)));
CompletableFuture.runAsync(() -> { // CompletableFuture.runAsync(() -> {
CompletableFuture.allOf(chunks.toArray(CompletableFuture[]::new)).join(); // CompletableFuture.allOf(chunks.toArray(CompletableFuture[]::new)).join();
System.out.println("load end"); // System.out.println("load end");
}); // });
inventory = new Inventory(InventoryType.CHEST_1_ROW, Component.text("Test inventory")); inventory = new Inventory(InventoryType.CHEST_1_ROW, Component.text("Test inventory"));
inventory.setItemStack(3, ItemStack.of(Material.DIAMOND, 34)); inventory.setItemStack(3, ItemStack.of(Material.DIAMOND, 34));

View File

@ -709,11 +709,11 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
if (!iter.hasNext()) return TaskSchedule.stop(); if (!iter.hasNext()) return TaskSchedule.stop();
var next = iter.next(); var next = iter.nextLong();
chunkAdder.accept(ChunkUtils.getChunkCoordX(next), ChunkUtils.getChunkCoordZ(next)); chunkAdder.accept(ChunkUtils.getChunkCoordX(next), ChunkUtils.getChunkCoordZ(next));
} }
return TaskSchedule.nextTick(); return TaskSchedule.tick(20);
}; };
scheduler().submitTask(taskRunnable); scheduler().submitTask(taskRunnable);
} else { } else {

View File

@ -211,16 +211,44 @@ public class DynamicChunk extends Chunk {
})); }));
} }
if (this instanceof LightingChunk light) {
if (light.lightCache.isValid()) {
return new ChunkDataPacket(chunkX, chunkZ,
new ChunkData(heightmapsNBT, data, entries),
createLightData(true));
} else {
// System.out.println("Regenerating light for chunk " + chunkX + " " + chunkZ);
LightingChunk.updateAfterGeneration(light);
return new ChunkDataPacket(chunkX, chunkZ,
new ChunkData(heightmapsNBT, data, entries),
createEmptyLight());
}
}
return new ChunkDataPacket(chunkX, chunkZ, return new ChunkDataPacket(chunkX, chunkZ,
new ChunkData(heightmapsNBT, data, entries), new ChunkData(heightmapsNBT, data, entries),
createLightData(true)); createLightData(true)
);
} }
@NotNull UpdateLightPacket createLightPacket() { @NotNull UpdateLightPacket createLightPacket() {
return new UpdateLightPacket(chunkX, chunkZ, createLightData(false)); return new UpdateLightPacket(chunkX, chunkZ, createLightData(false));
} }
protected LightData createLightData(boolean sendAll) { private LightData createEmptyLight() {
BitSet skyMask = new BitSet();
BitSet blockMask = new BitSet();
BitSet emptySkyMask = new BitSet();
BitSet emptyBlockMask = new BitSet();
List<byte[]> skyLights = new ArrayList<>();
List<byte[]> blockLights = new ArrayList<>();
return new LightData(skyMask, blockMask,
emptySkyMask, emptyBlockMask,
skyLights, blockLights);
}
protected LightData createLightData(boolean sendLater) {
BitSet skyMask = new BitSet(); BitSet skyMask = new BitSet();
BitSet blockMask = new BitSet(); BitSet blockMask = new BitSet();
BitSet emptySkyMask = new BitSet(); BitSet emptySkyMask = new BitSet();

View File

@ -1,5 +1,7 @@
package net.minestom.server.instance; package net.minestom.server.instance;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.collision.Shape; import net.minestom.server.collision.Shape;
import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Point;
@ -17,7 +19,6 @@ import net.minestom.server.utils.chunk.ChunkUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -26,6 +27,10 @@ import java.util.stream.Stream;
import static net.minestom.server.instance.light.LightCompute.emptyContent; import static net.minestom.server.instance.light.LightCompute.emptyContent;
public class LightingChunk extends DynamicChunk { public class LightingChunk extends DynamicChunk {
private static final int LIGHTING_CHUNKS_PER_SEND = Integer.getInteger("minestom.lighting.chunks-per-send", 10);
private static final int LIGHTING_CHUNKS_SEND_DELAY = Integer.getInteger("minestom.lighting.chunks-send-delay", 100);
private int[] heightmap; private int[] heightmap;
final CachedPacket lightCache = new CachedPacket(this::createLightPacket); final CachedPacket lightCache = new CachedPacket(this::createLightPacket);
boolean sendNeighbours = true; boolean sendNeighbours = true;
@ -81,9 +86,9 @@ public class LightingChunk extends DynamicChunk {
Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j); Chunk neighborChunk = instance.getChunk(chunkX + i, chunkZ + j);
if (neighborChunk == null) continue; if (neighborChunk == null) continue;
if (neighborChunk instanceof LightingChunk lightingChunk) { if (neighborChunk instanceof LightingChunk light) {
lightingChunk.lightCache.invalidate(); light.lightCache.invalidate();
lightingChunk.chunkCache.invalidate(); light.chunkCache.invalidate();
} }
for (int k = -1; k <= 1; k++) { for (int k = -1; k <= 1; k++) {
@ -115,7 +120,7 @@ public class LightingChunk extends DynamicChunk {
@Override @Override
protected void onLoad() { protected void onLoad() {
// Prefetch the chunk packet so that lazy lighting is computed // Prefetch the chunk packet so that lazy lighting is computed
chunkCache.body(); updateAfterGeneration(this);
} }
public int[] calculateHeightMap() { public int[] calculateHeightMap() {
@ -144,7 +149,7 @@ public class LightingChunk extends DynamicChunk {
} }
@Override @Override
protected LightData createLightData(boolean sendAll) { protected LightData createLightData(boolean sendLater) {
BitSet skyMask = new BitSet(); BitSet skyMask = new BitSet();
BitSet blockMask = new BitSet(); BitSet blockMask = new BitSet();
BitSet emptySkyMask = new BitSet(); BitSet emptySkyMask = new BitSet();
@ -176,10 +181,8 @@ public class LightingChunk extends DynamicChunk {
final byte[] skyLight = section.skyLight().array(); final byte[] skyLight = section.skyLight().array();
final byte[] blockLight = section.blockLight().array(); final byte[] blockLight = section.blockLight().array();
// System.out.println("Relit sky: " + wasUpdatedSky + " block: " + wasUpdatedBlock + " for section " + (index + minSection) + " in chunk " + chunkX + " " + chunkZ); if ((wasUpdatedSky || sendLater) && this.instance.getDimensionType().isSkylightEnabled()) {
if (skyLight.length != 0 && skyLight != emptyContent) {
if ((wasUpdatedSky || (sendAll && skyLight != emptyContent)) && this.instance.getDimensionType().isSkylightEnabled()) {
if (skyLight.length != 0) {
skyLights.add(skyLight); skyLights.add(skyLight);
skyMask.set(index); skyMask.set(index);
} else { } else {
@ -187,8 +190,8 @@ public class LightingChunk extends DynamicChunk {
} }
} }
if (wasUpdatedBlock || (sendAll && blockLight != emptyContent)) { if (wasUpdatedBlock || sendLater) {
if (blockLight.length != 0) { if (blockLight.length != 0 && blockLight != emptyContent) {
blockLights.add(blockLight); blockLights.add(blockLight);
blockMask.set(index); blockMask.set(index);
} else { } else {
@ -202,49 +205,88 @@ public class LightingChunk extends DynamicChunk {
sendNeighbours = false; sendNeighbours = false;
} }
return new LightData( return new LightData(skyMask, blockMask,
skyMask, blockMask,
emptySkyMask, emptyBlockMask, emptySkyMask, emptyBlockMask,
skyLights, blockLights skyLights, blockLights);
);
} }
private static final Set<LightingChunk> sendQueue = ConcurrentHashMap.newKeySet(); private static final LongSet queuedChunks = new LongOpenHashSet();
private static final ReentrantLock sendQueueLock = new ReentrantLock(); private static final List<LightingChunk> sendQueue = new ArrayList<>();
private static Task sendingTask = null; private static Task sendingTask = null;
private static final ReentrantLock lightLock = new ReentrantLock();
private static final ReentrantLock queueLock = new ReentrantLock();
private static void updateAfterGeneration(LightingChunk chunk) { static void updateAfterGeneration(LightingChunk chunk) {
for (int i = -1; i <= 1; i++) { for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) { for (int j = -1; j <= 1; j++) {
Chunk neighborChunk = chunk.instance.getChunk(chunk.chunkX + i, chunk.chunkZ + j); Chunk neighborChunk = chunk.instance.getChunk(chunk.chunkX + i, chunk.chunkZ + j);
if (neighborChunk == null) continue; if (neighborChunk == null) continue;
if (neighborChunk instanceof LightingChunk lightingChunk) { if (neighborChunk instanceof LightingChunk lightingChunk) {
sendQueue.add(lightingChunk); queueLock.lock();
if (queuedChunks.add(ChunkUtils.getChunkIndex(lightingChunk.chunkX, lightingChunk.chunkZ))) {
sendQueue.add(lightingChunk);
}
queueLock.unlock();
} }
} }
} }
sendQueueLock.lock(); lightLock.lock();
if (sendingTask != null) sendingTask.cancel(); if (sendingTask != null) {
lightLock.unlock();
return;
}
sendingTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> { sendingTask = MinecraftServer.getSchedulerManager().scheduleTask(() -> {
sendingTask = null; queueLock.lock();
var copy = new ArrayList<>(sendQueue);
sendQueue.clear();
queuedChunks.clear();
queueLock.unlock();
for (LightingChunk f : sendQueue) { // if (copy.size() != 0) {
// System.out.println("Sending lighting for " + copy.size() + " chunks");
// }
int count = 0;
for (LightingChunk f : copy) {
f.sections.forEach(s -> {
s.blockLight().invalidate();
s.skyLight().invalidate();
});
f.chunkCache.invalidate();
f.lightCache.invalidate();
}
// Load all the lighting
for (LightingChunk f : copy) {
if (f.isLoaded()) { if (f.isLoaded()) {
f.sections.forEach(s -> { f.lightCache.body();
s.blockLight().invalidate();
s.skyLight().invalidate();
});
f.chunkCache.invalidate();
f.lightCache.invalidate();
f.sendLighting();
} }
} }
sendQueue.clear();
}, TaskSchedule.tick(10), TaskSchedule.stop(), ExecutionType.ASYNC); // Send it slowly
sendQueueLock.unlock(); for (LightingChunk f : copy) {
if (f.isLoaded()) {
f.sendLighting();
if (f.getViewers().size() == 0) return;
}
count++;
if (count % LIGHTING_CHUNKS_PER_SEND == 0) {
// System.out.println("Sent " + count + " lighting chunks " + (count * 100 / copy.size()) + "%");
try {
Thread.sleep(LIGHTING_CHUNKS_SEND_DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, TaskSchedule.immediate(), TaskSchedule.tick(20), ExecutionType.ASYNC);
lightLock.unlock();
} }
private static void flushQueue(Instance instance, Set<Point> queue, LightType type) { private static void flushQueue(Instance instance, Set<Point> queue, LightType type) {

View File

@ -54,4 +54,8 @@ public final class CachedPacket implements SendablePacket {
} }
return cache; return cache;
} }
public boolean isValid() {
return packet != null && packet.get() != null;
}
} }