mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-03 05:26:50 +01:00
bc127ea819
Upstream has released updates that appear to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: eec4aab0 SPIGOT-6657: Add getPlayer to SheepDyeWoolEvent 205213c6 SPIGOT-6656: CauldronLevelChangeEvent is not fired correctly when dripstone fills the cauldron CraftBukkit Changes: b8c522d5 SPIGOT-6657: Add getPlayer to SheepDyeWoolEvent f04a77dc SPIGOT-6656: CauldronLevelChangeEvent is not fired correctly when dripstone fills the cauldron d1dbcebc SPIGOT-6653: Canceling snow bucket placement removes snow from bucket 4f34a67b #891: Fix scheduler task ID overflow and duplication issues Spigot Changes: d03d7f12 BUILDTOOLS-604: Rebuild patches
1316 lines
70 KiB
Diff
1316 lines
70 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
Date: Thu, 4 Jun 2020 22:43:29 -0400
|
|
Subject: [PATCH] Optimize Light Engine
|
|
|
|
Massive update to light to improve performance and chunk loading/generation.
|
|
|
|
1) Massive bit packing/unpacking optimizations and inlining.
|
|
A lot of performance has to do with constant packing and unpacking of bits.
|
|
We now inline a most bit operations, and re-use base x/y/z bits in many places.
|
|
This helps with cpu level processing to just do all the math at once instead
|
|
of having to jump in and out of function calls.
|
|
|
|
This much logic also is likely over the JVM Inline limit for JIT too.
|
|
2) Applied a few of JellySquid's Phosphor mod optimizations such as
|
|
- ensuring we don't notify neighbor chunks when neighbor chunk doesn't need to be notified
|
|
- reduce hasLight checks in initializing light, and prob some more, they are tagged JellySquid where phosphor influence was used.
|
|
3) Optimize hot path accesses to getting updating chunk to have less branching
|
|
4) Optimize getBlock accesses to have less branching, and less unpacking
|
|
5) Have a separate urgent bucket for chunk light tasks. These tasks will always cut in line over non blocking light tasks.
|
|
6) Retain chunk priority while light tasks are enqueued. So if a task comes in at high priority but the queue is full
|
|
of tasks already at a lower priority, before the task was simply added to the end. Now it can cut in line to the front.
|
|
this applies for both urgent and non urgent tasks.
|
|
7) Buffer non urgent tasks even if queueUpdate is called multiple times to improve efficiency.
|
|
8) Fix NPE risk that crashes server in getting nibble data
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
index 4114c0fa58e41383b5469cbc6abb44416a7af247..e649e6a8b354be45eed808ee02082ca7c826b59b 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
|
@@ -771,6 +771,7 @@ public class ChunkHolder {
|
|
ioPriority = com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY;
|
|
}
|
|
chunkMap.level.asyncChunkTaskManager.raisePriority(pos.x, pos.z, ioPriority);
|
|
+ chunkMap.level.getChunkSource().getLightEngine().queue.changePriority(pos.toLong(), this.queueLevel, priority); // Paper
|
|
}
|
|
if (this.queueLevel != priority) {
|
|
this.onLevelChange.onLevelChange(this.pos, () -> this.queueLevel, priority, p -> this.queueLevel = p); // use preferred priority
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
index 017ed8e1db45d49de67b17b5979721cfd57fb9c7..42fd259f4492e539112b5bcb310aaaadab58a443 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -97,6 +97,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana
|
|
import net.minecraft.world.level.storage.DimensionDataStorage;
|
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
|
import net.minecraft.world.phys.Vec3;
|
|
+import net.minecraft.world.level.storage.PrimaryLevelData;
|
|
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; // Paper
|
|
import org.apache.commons.lang3.mutable.MutableBoolean;
|
|
import org.apache.logging.log4j.LogManager;
|
|
@@ -338,6 +339,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
// Paper end
|
|
|
|
+ private final java.util.concurrent.ExecutorService lightThread; // Paper
|
|
public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureManager structureManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
|
|
super(new File(session.getDimensionPath(world.dimension()), "region"), dataFixer, dsync);
|
|
//this.visibleChunks = this.updatingChunks.clone(); // Paper - no more cloning
|
|
@@ -372,7 +374,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
this.progressListener = worldGenerationProgressListener;
|
|
this.chunkStatusListener = chunkStatusChangeListener;
|
|
- ProcessorMailbox<Runnable> lightthreaded; ProcessorMailbox<Runnable> threadedmailbox1 = lightthreaded = ProcessorMailbox.create(executor, "light"); // Paper
|
|
+ // Paper start - use light thread
|
|
+ ProcessorMailbox<Runnable> lightthreaded; ProcessorMailbox<Runnable> threadedmailbox1 = lightthreaded = ProcessorMailbox.create(lightThread = java.util.concurrent.Executors.newSingleThreadExecutor(r -> {
|
|
+ Thread thread = new Thread(r);
|
|
+ thread.setName(((PrimaryLevelData)level.getLevelData()).getLevelName() + " - Light");
|
|
+ thread.setDaemon(true);
|
|
+ thread.setPriority(Thread.NORM_PRIORITY+1);
|
|
+ return thread;
|
|
+ }), "light");
|
|
+ // Paper end
|
|
|
|
this.queueSorter = new ChunkTaskPriorityQueueSorter(ImmutableList.of(threadedmailbox, mailbox, threadedmailbox1), executor, Integer.MAX_VALUE);
|
|
this.worldgenMailbox = this.queueSorter.getProcessor(threadedmailbox, false);
|
|
@@ -881,6 +891,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
@Override
|
|
public void close() throws IOException {
|
|
try {
|
|
+ this.lightThread.shutdown(); // Paper
|
|
this.queueSorter.close();
|
|
this.level.asyncChunkTaskManager.close(true); // Paper - Required since we're closing regionfiles in the next line
|
|
this.poiManager.close();
|
|
diff --git a/src/main/java/net/minecraft/server/level/SectionTracker.java b/src/main/java/net/minecraft/server/level/SectionTracker.java
|
|
index d3d6651eb51c852ed1d1eeb5689569d5308b246d..c2d36600a0081c78425868154bdcf7f42d549154 100644
|
|
--- a/src/main/java/net/minecraft/server/level/SectionTracker.java
|
|
+++ b/src/main/java/net/minecraft/server/level/SectionTracker.java
|
|
@@ -15,13 +15,19 @@ public abstract class SectionTracker extends DynamicGraphMinFixedPoint {
|
|
|
|
@Override
|
|
protected void checkNeighborsAfterUpdate(long id, int level, boolean decrease) {
|
|
+ // Paper start
|
|
+ int x = (int) (id >> 42);
|
|
+ int y = (int) (id << 44 >> 44);
|
|
+ int z = (int) (id << 22 >> 42);
|
|
+ // Paper end
|
|
for(int i = -1; i <= 1; ++i) {
|
|
for(int j = -1; j <= 1; ++j) {
|
|
for(int k = -1; k <= 1; ++k) {
|
|
- long l = SectionPos.offset(id, i, j, k);
|
|
- if (l != id) {
|
|
+ if (i == 0 && j == 0 && k == 0) continue; // Paper
|
|
+ long l = (((long) (x + i) & 4194303L) << 42) | (((long) (y + j) & 1048575L)) | (((long) (z + k) & 4194303L) << 20);
|
|
+ // if (l != id) { // Paper - checked above
|
|
this.checkNeighbor(id, l, level, decrease);
|
|
- }
|
|
+ //} // Paper
|
|
}
|
|
}
|
|
}
|
|
@@ -32,10 +38,15 @@ public abstract class SectionTracker extends DynamicGraphMinFixedPoint {
|
|
protected int getComputedLevel(long id, long excludedId, int maxLevel) {
|
|
int i = maxLevel;
|
|
|
|
+ // Paper start
|
|
+ int x = (int) (id >> 42);
|
|
+ int y = (int) (id << 44 >> 44);
|
|
+ int z = (int) (id << 22 >> 42);
|
|
+ // Paper end
|
|
for(int j = -1; j <= 1; ++j) {
|
|
for(int k = -1; k <= 1; ++k) {
|
|
for(int l = -1; l <= 1; ++l) {
|
|
- long m = SectionPos.offset(id, j, k, l);
|
|
+ long m = (((long) (x + j) & 4194303L) << 42) | (((long) (y + k) & 1048575L)) | (((long) (z + l) & 4194303L) << 20); // Paper
|
|
if (m == id) {
|
|
m = Long.MAX_VALUE;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
index 5734a127fece27cbd20fdc31b18b9c10ab89b3ce..677aaeab867f1f4159b6fa023ce84e3dca4795fc 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -1069,7 +1069,7 @@ public class ServerChunkCache extends ChunkSource {
|
|
if (ServerChunkCache.this.runDistanceManagerUpdates()) {
|
|
return true;
|
|
} else {
|
|
- ServerChunkCache.this.lightEngine.tryScheduleUpdate();
|
|
+ ServerChunkCache.this.lightEngine.tryScheduleUpdate(); // Paper - not needed
|
|
return super.pollTask() || execChunkTask; // Paper
|
|
}
|
|
} finally {
|
|
diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
|
|
index 2c4aa4b66d83b6e7a104479860b7982629c63c3b..f0df7b2bd618c7be18c4c86f735b303dc73d98ad 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
|
|
@@ -1,6 +1,7 @@
|
|
package net.minecraft.server.level;
|
|
|
|
import com.mojang.datafixers.util.Pair;
|
|
+import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; // Paper
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
import it.unimi.dsi.fastutil.objects.ObjectList;
|
|
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
|
|
@@ -16,6 +17,7 @@ import net.minecraft.util.thread.ProcessorMailbox;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.LightLayer;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
+import net.minecraft.world.level.chunk.ChunkStatus;
|
|
import net.minecraft.world.level.chunk.DataLayer;
|
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
import net.minecraft.world.level.chunk.LightChunkGetter;
|
|
@@ -26,15 +28,149 @@ import org.apache.logging.log4j.Logger;
|
|
public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable {
|
|
private static final Logger LOGGER = LogManager.getLogger();
|
|
private final ProcessorMailbox<Runnable> taskMailbox;
|
|
- private final ObjectList<Pair<ThreadedLevelLightEngine.TaskType, Runnable>> lightTasks = new ObjectArrayList<>();
|
|
- private final ChunkMap chunkMap;
|
|
+ // Paper start
|
|
+ private static final int MAX_PRIORITIES = ChunkMap.MAX_CHUNK_DISTANCE + 2;
|
|
+
|
|
+ private boolean isChunkLightStatus(long pair) {
|
|
+ ChunkHolder playerChunk = playerChunkMap.getVisibleChunkIfPresent(pair);
|
|
+ if (playerChunk == null) {
|
|
+ return false;
|
|
+ }
|
|
+ ChunkStatus status = ChunkHolder.getStatus(playerChunk.getTicketLevel());
|
|
+ return status != null && status.isOrAfter(ChunkStatus.LIGHT);
|
|
+ }
|
|
+
|
|
+ static class ChunkLightQueue {
|
|
+ public boolean shouldFastUpdate;
|
|
+ java.util.ArrayDeque<Runnable> pre = new java.util.ArrayDeque<Runnable>();
|
|
+ java.util.ArrayDeque<Runnable> post = new java.util.ArrayDeque<Runnable>();
|
|
+
|
|
+ ChunkLightQueue(long chunk) {}
|
|
+ }
|
|
+
|
|
+ static class PendingLightTask {
|
|
+ long chunkId;
|
|
+ IntSupplier priority;
|
|
+ Runnable pre;
|
|
+ Runnable post;
|
|
+ boolean fastUpdate;
|
|
+
|
|
+ public PendingLightTask(long chunkId, IntSupplier priority, Runnable pre, Runnable post, boolean fastUpdate) {
|
|
+ this.chunkId = chunkId;
|
|
+ this.priority = priority;
|
|
+ this.pre = pre;
|
|
+ this.post = post;
|
|
+ this.fastUpdate = fastUpdate;
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ // Retain the chunks priority level for queued light tasks
|
|
+ class LightQueue {
|
|
+ private int size = 0;
|
|
+ private final Long2ObjectLinkedOpenHashMap<ChunkLightQueue>[] buckets = new Long2ObjectLinkedOpenHashMap[MAX_PRIORITIES];
|
|
+ private final java.util.concurrent.ConcurrentLinkedQueue<PendingLightTask> pendingTasks = new java.util.concurrent.ConcurrentLinkedQueue<>();
|
|
+ private final java.util.concurrent.ConcurrentLinkedQueue<Runnable> priorityChanges = new java.util.concurrent.ConcurrentLinkedQueue<>();
|
|
+
|
|
+ private LightQueue() {
|
|
+ for (int i = 0; i < buckets.length; i++) {
|
|
+ buckets[i] = new Long2ObjectLinkedOpenHashMap<>();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void changePriority(long pair, int currentPriority, int priority) {
|
|
+ this.priorityChanges.add(() -> {
|
|
+ ChunkLightQueue remove = this.buckets[currentPriority].remove(pair);
|
|
+ if (remove != null) {
|
|
+ ChunkLightQueue existing = this.buckets[Math.max(1, priority)].put(pair, remove);
|
|
+ if (existing != null) {
|
|
+ remove.pre.addAll(existing.pre);
|
|
+ remove.post.addAll(existing.post);
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+
|
|
+ public final void addChunk(long chunkId, IntSupplier priority, Runnable pre, Runnable post) {
|
|
+ pendingTasks.add(new PendingLightTask(chunkId, priority, pre, post, true));
|
|
+ tryScheduleUpdate();
|
|
+ }
|
|
+
|
|
+ public final void add(long chunkId, IntSupplier priority, ThreadedLevelLightEngine.TaskType type, Runnable run) {
|
|
+ pendingTasks.add(new PendingLightTask(chunkId, priority, type == TaskType.PRE_UPDATE ? run : null, type == TaskType.POST_UPDATE ? run : null, false));
|
|
+ }
|
|
+ public final void add(PendingLightTask update) {
|
|
+ int priority = update.priority.getAsInt();
|
|
+ ChunkLightQueue lightQueue = this.buckets[priority].computeIfAbsent(update.chunkId, ChunkLightQueue::new);
|
|
+
|
|
+ if (update.pre != null) {
|
|
+ this.size++;
|
|
+ lightQueue.pre.add(update.pre);
|
|
+ }
|
|
+ if (update.post != null) {
|
|
+ this.size++;
|
|
+ lightQueue.post.add(update.post);
|
|
+ }
|
|
+ if (update.fastUpdate) {
|
|
+ lightQueue.shouldFastUpdate = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public final boolean isEmpty() {
|
|
+ return this.size == 0 && this.pendingTasks.isEmpty();
|
|
+ }
|
|
+
|
|
+ public final int size() {
|
|
+ return this.size;
|
|
+ }
|
|
+
|
|
+ public boolean poll(java.util.List<Runnable> pre, java.util.List<Runnable> post) {
|
|
+ PendingLightTask pending;
|
|
+ while ((pending = pendingTasks.poll()) != null) {
|
|
+ add(pending);
|
|
+ }
|
|
+ Runnable run;
|
|
+ while ((run = priorityChanges.poll()) != null) {
|
|
+ run.run();
|
|
+ }
|
|
+ boolean hasWork = false;
|
|
+ Long2ObjectLinkedOpenHashMap<ChunkLightQueue>[] buckets = this.buckets;
|
|
+ int priority = 0;
|
|
+ while (priority < MAX_PRIORITIES && !isEmpty()) {
|
|
+ Long2ObjectLinkedOpenHashMap<ChunkLightQueue> bucket = buckets[priority];
|
|
+ if (bucket.isEmpty()) {
|
|
+ priority++;
|
|
+ if (hasWork) {
|
|
+ return true;
|
|
+ } else {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ ChunkLightQueue queue = bucket.removeFirst();
|
|
+ this.size -= queue.pre.size() + queue.post.size();
|
|
+ pre.addAll(queue.pre);
|
|
+ post.addAll(queue.post);
|
|
+ queue.pre.clear();
|
|
+ queue.post.clear();
|
|
+ hasWork = true;
|
|
+ if (queue.shouldFastUpdate) {
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return hasWork;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ final LightQueue queue = new LightQueue();
|
|
+ // Paper end
|
|
+ private final ChunkMap chunkMap; private final ChunkMap playerChunkMap; // Paper
|
|
private final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> sorterMailbox;
|
|
private volatile int taskPerBatch = 5;
|
|
private final AtomicBoolean scheduled = new AtomicBoolean();
|
|
|
|
public ThreadedLevelLightEngine(LightChunkGetter chunkProvider, ChunkMap chunkStorage, boolean hasBlockLight, ProcessorMailbox<Runnable> processor, ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> executor) {
|
|
super(chunkProvider, true, hasBlockLight);
|
|
- this.chunkMap = chunkStorage;
|
|
+ this.chunkMap = chunkStorage; this.playerChunkMap = chunkMap; // Paper
|
|
this.sorterMailbox = executor;
|
|
this.taskMailbox = processor;
|
|
}
|
|
@@ -120,13 +256,9 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
|
|
}
|
|
|
|
private void addTask(int x, int z, IntSupplier completedLevelSupplier, ThreadedLevelLightEngine.TaskType stage, Runnable task) {
|
|
- this.sorterMailbox.tell(ChunkTaskPriorityQueueSorter.message(() -> {
|
|
- this.lightTasks.add(Pair.of(stage, task));
|
|
- if (this.lightTasks.size() >= this.taskPerBatch) {
|
|
- this.runUpdate();
|
|
- }
|
|
-
|
|
- }, ChunkPos.asLong(x, z), completedLevelSupplier));
|
|
+ // Paper start - replace method
|
|
+ this.queue.add(ChunkPos.asLong(x, z), completedLevelSupplier, stage, task);
|
|
+ // Paper end
|
|
}
|
|
|
|
@Override
|
|
@@ -142,8 +274,19 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
|
|
|
|
public CompletableFuture<ChunkAccess> lightChunk(ChunkAccess chunk, boolean excludeBlocks) {
|
|
ChunkPos chunkPos = chunk.getPos();
|
|
- chunk.setLightCorrect(false);
|
|
- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> {
|
|
+ // Paper start
|
|
+ //ichunkaccess.b(false); // Don't need to disable this
|
|
+ long pair = chunkPos.toLong();
|
|
+ CompletableFuture<ChunkAccess> future = new CompletableFuture<>();
|
|
+ IntSupplier prioritySupplier = playerChunkMap.getChunkQueueLevel(pair);
|
|
+ boolean[] skippedPre = {false};
|
|
+ this.queue.addChunk(pair, prioritySupplier, Util.name(() -> {
|
|
+ if (!isChunkLightStatus(pair)) {
|
|
+ future.complete(chunk);
|
|
+ skippedPre[0] = true;
|
|
+ return;
|
|
+ }
|
|
+ // Paper end
|
|
LevelChunkSection[] levelChunkSections = chunk.getSections();
|
|
|
|
for(int i = 0; i < chunk.getSectionsCount(); ++i) {
|
|
@@ -163,51 +306,45 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
|
|
|
|
}, () -> {
|
|
return "lightChunk " + chunkPos + " " + excludeBlocks;
|
|
- }));
|
|
- return CompletableFuture.supplyAsync(() -> {
|
|
+ // Paper start - merge the 2 together
|
|
+ }), () -> {
|
|
+ this.chunkMap.releaseLightTicket(chunkPos); // Paper - moved from below, we want to call this even when returning early
|
|
+ if (skippedPre[0]) return; // Paper - future's already complete
|
|
chunk.setLightCorrect(true);
|
|
super.retainData(chunkPos, false);
|
|
- this.chunkMap.releaseLightTicket(chunkPos);
|
|
- return chunk;
|
|
- }, (runnable) -> {
|
|
- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, runnable);
|
|
+ //this.chunkMap.releaseLightTicket(chunkPos); // Paper - moved up
|
|
+ future.complete(chunk);
|
|
});
|
|
+ return future;
|
|
+ // Paper end
|
|
}
|
|
|
|
public void tryScheduleUpdate() {
|
|
- if ((!this.lightTasks.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) {
|
|
+ if ((!this.queue.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) { // Paper
|
|
this.taskMailbox.tell(() -> {
|
|
this.runUpdate();
|
|
this.scheduled.set(false);
|
|
+ tryScheduleUpdate(); // Paper - if we still have work to do, do it!
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
+ // Paper start - replace impl
|
|
+ private final java.util.List<Runnable> pre = new java.util.ArrayList<>();
|
|
+ private final java.util.List<Runnable> post = new java.util.ArrayList<>();
|
|
private void runUpdate() {
|
|
- int i = Math.min(this.lightTasks.size(), this.taskPerBatch);
|
|
- ObjectListIterator<Pair<ThreadedLevelLightEngine.TaskType, Runnable>> objectListIterator = this.lightTasks.iterator();
|
|
-
|
|
- int j;
|
|
- for(j = 0; objectListIterator.hasNext() && j < i; ++j) {
|
|
- Pair<ThreadedLevelLightEngine.TaskType, Runnable> pair = objectListIterator.next();
|
|
- if (pair.getFirst() == ThreadedLevelLightEngine.TaskType.PRE_UPDATE) {
|
|
- pair.getSecond().run();
|
|
- }
|
|
+ if (queue.poll(pre, post)) {
|
|
+ pre.forEach(Runnable::run);
|
|
+ pre.clear();
|
|
+ super.runUpdates(Integer.MAX_VALUE, true, true);
|
|
+ post.forEach(Runnable::run);
|
|
+ post.clear();
|
|
+ } else {
|
|
+ // might have level updates to go still
|
|
+ super.runUpdates(Integer.MAX_VALUE, true, true);
|
|
}
|
|
-
|
|
- objectListIterator.back(j);
|
|
- super.runUpdates(Integer.MAX_VALUE, true, true);
|
|
-
|
|
- for(int var5 = 0; objectListIterator.hasNext() && var5 < i; ++var5) {
|
|
- Pair<ThreadedLevelLightEngine.TaskType, Runnable> pair2 = objectListIterator.next();
|
|
- if (pair2.getFirst() == ThreadedLevelLightEngine.TaskType.POST_UPDATE) {
|
|
- pair2.getSecond().run();
|
|
- }
|
|
-
|
|
- objectListIterator.remove();
|
|
- }
|
|
-
|
|
+ // Paper end
|
|
}
|
|
|
|
public void setTaskPerBatch(int taskBatchSize) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java
|
|
index c561d69b4b903cd3625468b239cb1ace3e317700..c4acc7e0035d37167cf5a56f8d90ba17f85cbbe3 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java
|
|
@@ -14,6 +14,13 @@ public class DataLayer {
|
|
@Nullable
|
|
protected byte[] data;
|
|
// Paper start
|
|
+ public static final DataLayer EMPTY_NIBBLE_ARRAY = new DataLayer() {
|
|
+ @Override
|
|
+ public byte[] getData() {
|
|
+ throw new IllegalStateException();
|
|
+ }
|
|
+ };
|
|
+ public long lightCacheKey = Long.MIN_VALUE;
|
|
public static byte[] EMPTY_NIBBLE = new byte[2048];
|
|
private static final int nibbleBucketSizeMultiplier = Integer.getInteger("Paper.nibbleBucketSize", 3072);
|
|
private static final int maxPoolSize = Integer.getInteger("Paper.maxNibblePoolSize", (int) Math.min(6, Math.max(1, Runtime.getRuntime().maxMemory() / 1024 / 1024 / 1024)) * (nibbleBucketSizeMultiplier * 8));
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java
|
|
index 37d7165dfd17da03428f8dbbbf95aa8005be289c..63fb8ccd47c21b4ef332daf418c9f70f2248be45 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java
|
|
@@ -22,9 +22,11 @@ public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionSt
|
|
}
|
|
|
|
private int getLightEmission(long blockPos) {
|
|
- int i = BlockPos.getX(blockPos);
|
|
- int j = BlockPos.getY(blockPos);
|
|
- int k = BlockPos.getZ(blockPos);
|
|
+ // Paper start - inline math
|
|
+ int i = (int) (blockPos >> 38);
|
|
+ int j = (int) ((blockPos << 52) >> 52);
|
|
+ int k = (int) ((blockPos << 26) >> 38);
|
|
+ // Paper end
|
|
BlockGetter blockGetter = this.chunkSource.getChunkForLighting(SectionPos.blockToSectionCoord(i), SectionPos.blockToSectionCoord(k));
|
|
return blockGetter != null ? blockGetter.getLightEmission(this.pos.set(i, j, k)) : 0;
|
|
}
|
|
@@ -38,22 +40,33 @@ public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionSt
|
|
} else if (level >= 15) {
|
|
return level;
|
|
} else {
|
|
- int i = Integer.signum(BlockPos.getX(targetId) - BlockPos.getX(sourceId));
|
|
- int j = Integer.signum(BlockPos.getY(targetId) - BlockPos.getY(sourceId));
|
|
- int k = Integer.signum(BlockPos.getZ(targetId) - BlockPos.getZ(sourceId));
|
|
+ // Paper start
|
|
+ int ix = (int) (targetId >> 38);
|
|
+ int iy = (int) ((targetId << 52) >> 52);
|
|
+ int iz = (int) ((targetId << 26) >> 38);
|
|
+ int jx = (int) (sourceId >> 38);
|
|
+ int jy = (int) ((sourceId << 52) >> 52);
|
|
+ int jz = (int) ((sourceId << 26) >> 38);
|
|
+ int i = Integer.signum(ix - jx);
|
|
+ int j = Integer.signum(iy - jy);
|
|
+ int k = Integer.signum(iz - jz);
|
|
+ // Paper end
|
|
Direction direction = Direction.fromNormal(i, j, k);
|
|
if (direction == null) {
|
|
return 15;
|
|
} else {
|
|
//MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded
|
|
- BlockState blockState = this.getStateAndOpacity(targetId, mutableInt);
|
|
- if (mutableInt.getValue() >= 15) {
|
|
+ // Paper start
|
|
+ BlockState blockState = this.getBlockOptimized(ix, iy, iz, mutableInt);
|
|
+ int blockdLight = mutableInt.getValue();
|
|
+ if (blockdLight >= 15) {
|
|
+ // Paper end
|
|
return 15;
|
|
} else {
|
|
- BlockState blockState2 = this.getStateAndOpacity(sourceId, (MutableInt)null);
|
|
+ BlockState blockState2 = this.getBlockOptimized(jx, jy, jz); // Paper
|
|
VoxelShape voxelShape = this.getShape(blockState2, sourceId, direction);
|
|
VoxelShape voxelShape2 = this.getShape(blockState, targetId, direction.getOpposite());
|
|
- return Shapes.faceShapeOccludes(voxelShape, voxelShape2) ? 15 : level + Math.max(1, mutableInt.getValue());
|
|
+ return Shapes.faceShapeOccludes(voxelShape, voxelShape2) ? 15 : level + Math.max(1, blockdLight); // Paper
|
|
}
|
|
}
|
|
}
|
|
@@ -61,10 +74,15 @@ public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionSt
|
|
|
|
@Override
|
|
protected void checkNeighborsAfterUpdate(long id, int level, boolean decrease) {
|
|
- long l = SectionPos.blockToSection(id);
|
|
+ // Paper start - reuse unpacking, credit to JellySquid (Didn't do full optimization though)
|
|
+ int x = (int) (id >> 38);
|
|
+ int y = (int) ((id << 52) >> 52);
|
|
+ int z = (int) ((id << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(x, y, z);
|
|
+ // Paper end
|
|
|
|
for(Direction direction : DIRECTIONS) {
|
|
- long m = BlockPos.offset(id, direction);
|
|
+ long m = BlockPos.getAdjacent(x, y, z, direction); // Paper
|
|
long n = SectionPos.blockToSection(m);
|
|
if (l == n || this.storage.storingLightForSection(n)) {
|
|
this.checkNeighbor(id, m, level, decrease);
|
|
@@ -87,22 +105,32 @@ public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionSt
|
|
}
|
|
}
|
|
|
|
- long l = SectionPos.blockToSection(id);
|
|
- DataLayer dataLayer = this.storage.getDataLayer(l, true);
|
|
+ // Paper start
|
|
+ int baseX = (int) (id >> 38);
|
|
+ int baseY = (int) ((id << 52) >> 52);
|
|
+ int baseZ = (int) ((id << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ DataLayer dataLayer = this.storage.updating.getUpdatingOptimized(l);
|
|
+ // Paper end
|
|
|
|
for(Direction direction : DIRECTIONS) {
|
|
- long m = BlockPos.offset(id, direction);
|
|
+ // Paper start
|
|
+ int newX = baseX + direction.getStepX();
|
|
+ int newY = baseY + direction.getStepY();
|
|
+ int newZ = baseZ + direction.getStepZ();
|
|
+ long m = BlockPos.asLong(newX, newY, newZ);
|
|
if (m != excludedId) {
|
|
- long n = SectionPos.blockToSection(m);
|
|
+ long n = SectionPos.blockPosAsSectionLong(newX, newY, newZ);
|
|
+ // Paper end
|
|
DataLayer dataLayer2;
|
|
if (l == n) {
|
|
dataLayer2 = dataLayer;
|
|
} else {
|
|
- dataLayer2 = this.storage.getDataLayer(n, true);
|
|
+ dataLayer2 = this.storage.updating.getUpdatingOptimized(n); // Paper
|
|
}
|
|
|
|
if (dataLayer2 != null) {
|
|
- int k = this.computeLevelFromNeighbor(m, id, this.getLevel(dataLayer2, m));
|
|
+ int k = this.computeLevelFromNeighbor(m, id, this.getLevel(dataLayer2, newX, newY, newZ)); // Paper
|
|
if (i > k) {
|
|
i = k;
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java b/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java
|
|
index 314b46f0becd088d26956b45981217b128d539cb..d8e71e3759e02b3db5603302ad37656d0a72c2b2 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java
|
|
@@ -14,9 +14,14 @@ public class BlockLightSectionStorage extends LayerLightSectionStorage<BlockLigh
|
|
|
|
@Override
|
|
protected int getLightValue(long blockPos) {
|
|
- long l = SectionPos.blockToSection(blockPos);
|
|
- DataLayer dataLayer = this.getDataLayer(l, false);
|
|
- return dataLayer == null ? 0 : dataLayer.get(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)));
|
|
+ // Paper start
|
|
+ int baseX = (int) (blockPos >> 38);
|
|
+ int baseY = (int) ((blockPos << 52) >> 52);
|
|
+ int baseZ = (int) ((blockPos << 26) >> 38);
|
|
+ long l = net.minecraft.core.SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ DataLayer dataLayer = this.e_visible.lookup.apply(l);
|
|
+ return dataLayer == null ? 0 : dataLayer.get(baseX & 15, baseY & 15, baseZ & 15);
|
|
+ // Paper end
|
|
}
|
|
|
|
protected static final class BlockDataLayerStorageMap extends DataLayerStorageMap<BlockLightSectionStorage.BlockDataLayerStorageMap> {
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java b/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java
|
|
index 52682471adc13dffc0383fc4abacbd3397f3bb10..9d1bf2d68d8d630d5ecb0581df57a99eae9eff0d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java
|
|
@@ -6,13 +6,18 @@ import net.minecraft.world.level.chunk.DataLayer;
|
|
|
|
public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
private static final int CACHE_SIZE = 2;
|
|
- private final long[] lastSectionKeys = new long[2];
|
|
- private final DataLayer[] lastSections = new DataLayer[2];
|
|
+ // private final long[] b = new long[2]; // Paper - unused
|
|
+ private final DataLayer[] lastSections = new DataLayer[]{DataLayer.EMPTY_NIBBLE_ARRAY, DataLayer.EMPTY_NIBBLE_ARRAY}; private final DataLayer[] cache = lastSections; // Paper - OBFHELPER
|
|
private boolean cacheEnabled;
|
|
protected final com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<DataLayer> data; // Paper - avoid copying light data
|
|
protected final boolean isVisible; // Paper - avoid copying light data
|
|
- java.util.function.Function<Long, DataLayer> lookup; // Paper - faster branchless lookup
|
|
|
|
+ // Paper start - faster lookups with less branching, use interface to avoid boxing instead of Function
|
|
+ public final NibbleArrayAccess lookup;
|
|
+ public interface NibbleArrayAccess {
|
|
+ DataLayer apply(long id);
|
|
+ }
|
|
+ // Paper end
|
|
// Paper start - avoid copying light data
|
|
protected DataLayerStorageMap(com.destroystokyo.paper.util.map.QueuedChangesMapLong2Object<DataLayer> data, boolean isVisible) {
|
|
if (isVisible) {
|
|
@@ -20,12 +25,14 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
}
|
|
this.data = data;
|
|
this.isVisible = isVisible;
|
|
+ // Paper end - avoid copying light data
|
|
+ // Paper start - faster lookups with less branching
|
|
if (isVisible) {
|
|
lookup = data::getVisibleAsync;
|
|
} else {
|
|
- lookup = data::getUpdating;
|
|
+ lookup = data.getUpdatingMap()::get; // jump straight the sub map
|
|
}
|
|
- // Paper end - avoid copying light data
|
|
+ // Paper end
|
|
this.clearCache();
|
|
this.cacheEnabled = true;
|
|
}
|
|
@@ -35,7 +42,9 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
public void copyDataLayer(long pos) {
|
|
if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
|
|
DataLayer updating = this.data.getUpdating(pos); // Paper - pool nibbles
|
|
- this.data.queueUpdate(pos, new DataLayer().markPoolSafe(updating.getCloneIfSet())); // Paper - avoid copying light data - pool safe clone
|
|
+ DataLayer nibblearray = new DataLayer().markPoolSafe(updating.getCloneIfSet()); // Paper
|
|
+ nibblearray.lightCacheKey = pos; // Paper
|
|
+ this.data.queueUpdate(pos, nibblearray); // Paper - avoid copying light data - pool safe clone
|
|
if (updating.cleaner != null) net.minecraft.server.MCUtil.scheduleTask(2, updating.cleaner, "Light Engine Release"); // Paper - delay clean incase anything holding ref was still using it
|
|
this.clearCache();
|
|
}
|
|
@@ -44,33 +53,34 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
return lookup.apply(chunkPos) != null; // Paper - avoid copying light data
|
|
}
|
|
|
|
- @Nullable
|
|
- public final DataLayer getLayer(long chunkPos) { // Paper - final
|
|
- if (this.cacheEnabled) {
|
|
- for(int i = 0; i < 2; ++i) {
|
|
- if (chunkPos == this.lastSectionKeys[i]) {
|
|
- return this.lastSections[i];
|
|
- }
|
|
- }
|
|
- }
|
|
+ // Paper start - less branching as we know we are using cache and updating
|
|
+ public final DataLayer getUpdatingOptimized(final long chunkPos) { // Paper - final
|
|
+ final DataLayer[] cache = this.cache;
|
|
+ if (cache[0].lightCacheKey == chunkPos) return cache[0];
|
|
+ if (cache[1].lightCacheKey == chunkPos) return cache[1];
|
|
|
|
- DataLayer dataLayer = lookup.apply(chunkPos); // Paper - avoid copying light data
|
|
+ final DataLayer dataLayer = this.lookup.apply(chunkPos); // Paper - avoid copying light data
|
|
if (dataLayer == null) {
|
|
return null;
|
|
} else {
|
|
- if (this.cacheEnabled) {
|
|
- for(int j = 1; j > 0; --j) {
|
|
- this.lastSectionKeys[j] = this.lastSectionKeys[j - 1];
|
|
- this.lastSections[j] = this.lastSections[j - 1];
|
|
- }
|
|
-
|
|
- this.lastSectionKeys[0] = chunkPos;
|
|
- this.lastSections[0] = dataLayer;
|
|
- }
|
|
-
|
|
+ cache[1] = cache[0];
|
|
+ cache[0] = dataLayer;
|
|
return dataLayer;
|
|
}
|
|
}
|
|
+ // Paper end
|
|
+
|
|
+ @Nullable
|
|
+ public final DataLayer getLayer(final long chunkPos) { // Paper - final
|
|
+ // Paper start - optimize visible case or missed updating cases
|
|
+ if (this.cacheEnabled) {
|
|
+ // short circuit to optimized
|
|
+ return getUpdatingOptimized(chunkPos);
|
|
+ }
|
|
+
|
|
+ return this.lookup.apply(chunkPos);
|
|
+ // Paper end
|
|
+ }
|
|
|
|
@Nullable
|
|
public DataLayer removeLayer(long chunkPos) {
|
|
@@ -80,13 +90,14 @@ public abstract class DataLayerStorageMap<M extends DataLayerStorageMap<M>> {
|
|
|
|
public void setLayer(long pos, DataLayer data) {
|
|
if (this.isVisible) { throw new IllegalStateException("writing to visible data"); } // Paper - avoid copying light data
|
|
+ data.lightCacheKey = pos; // Paper
|
|
this.data.queueUpdate(pos, data); // Paper - avoid copying light data
|
|
}
|
|
|
|
public void clearCache() {
|
|
for(int i = 0; i < 2; ++i) {
|
|
- this.lastSectionKeys[i] = Long.MAX_VALUE;
|
|
- this.lastSections[i] = null;
|
|
+ // this.b[i] = Long.MAX_VALUE; // Paper - Unused
|
|
+ this.lastSections[i] = DataLayer.EMPTY_NIBBLE_ARRAY; // Paper
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java
|
|
index c5d0d8fb4c0344fbcc2ac87ba6d8027c29d51286..0952dde531aecd8ffc6eb72d4841362c5a6ebdcd 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java
|
|
@@ -10,6 +10,7 @@ import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.LightLayer;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
+import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.DataLayer;
|
|
import net.minecraft.world.level.chunk.LightChunkGetter;
|
|
import net.minecraft.world.phys.shapes.Shapes;
|
|
@@ -26,8 +27,35 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
protected final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
|
private static final int CACHE_SIZE = 2;
|
|
private final long[] lastChunkPos = new long[2];
|
|
- private final BlockGetter[] lastChunk = new BlockGetter[2];
|
|
+ private final ChunkAccess[] lastChunk = new ChunkAccess[2]; // Paper
|
|
|
|
+ // Paper start - see fully commented out method below (look for Bedrock)
|
|
+ // optimized method with less branching for when scenarios arent needed.
|
|
+ // avoid using mutable version if can
|
|
+ protected final BlockState getBlockOptimized(int x, int y, int z, MutableInt mutableint) {
|
|
+ ChunkAccess iblockaccess = this.getChunk(x >> 4, z >> 4);
|
|
+
|
|
+ if (iblockaccess == null) {
|
|
+ mutableint.setValue(16);
|
|
+ return Blocks.BEDROCK.defaultBlockState();
|
|
+ } else {
|
|
+ this.pos.set(x, y, z);
|
|
+ BlockState iblockdata = iblockaccess.getType(x, y, z);
|
|
+ mutableint.setValue(iblockdata.getLightBlock(this.chunkSource.getLevel(), this.pos));
|
|
+ return iblockdata.canOcclude() && iblockdata.useShapeForLightOcclusion() ? iblockdata : Blocks.AIR.defaultBlockState();
|
|
+ }
|
|
+ }
|
|
+ protected final BlockState getBlockOptimized(int x, int y, int z) {
|
|
+ ChunkAccess iblockaccess = this.getChunk(x >> 4, z >> 4);
|
|
+
|
|
+ if (iblockaccess == null) {
|
|
+ return Blocks.BEDROCK.defaultBlockState();
|
|
+ } else {
|
|
+ BlockState iblockdata = iblockaccess.getType(x, y, z);
|
|
+ return iblockdata.canOcclude() && iblockdata.useShapeForLightOcclusion() ? iblockdata : Blocks.AIR.defaultBlockState();
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
public LayerLightEngine(LightChunkGetter chunkProvider, LightLayer type, S lightStorage) {
|
|
super(16, 256, 8192);
|
|
this.chunkSource = chunkProvider;
|
|
@@ -46,7 +74,7 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
}
|
|
|
|
@Nullable
|
|
- private BlockGetter getChunk(int chunkX, int chunkZ) {
|
|
+ private ChunkAccess getChunk(int chunkX, int chunkZ) { // Paper
|
|
long l = ChunkPos.asLong(chunkX, chunkZ);
|
|
|
|
for(int i = 0; i < 2; ++i) {
|
|
@@ -55,7 +83,7 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
}
|
|
}
|
|
|
|
- BlockGetter blockGetter = this.chunkSource.getChunkForLighting(chunkX, chunkZ);
|
|
+ ChunkAccess blockGetter = (ChunkAccess) this.chunkSource.getChunkForLighting(chunkX, chunkZ); // Paper
|
|
|
|
for(int j = 1; j > 0; --j) {
|
|
this.lastChunkPos[j] = this.lastChunkPos[j - 1];
|
|
@@ -72,35 +100,37 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
Arrays.fill(this.lastChunk, (Object)null);
|
|
}
|
|
|
|
- protected BlockState getStateAndOpacity(long pos, @Nullable MutableInt mutableInt) {
|
|
- if (pos == Long.MAX_VALUE) {
|
|
- if (mutableInt != null) {
|
|
- mutableInt.setValue(0);
|
|
- }
|
|
-
|
|
- return Blocks.AIR.defaultBlockState();
|
|
- } else {
|
|
- int i = SectionPos.blockToSectionCoord(BlockPos.getX(pos));
|
|
- int j = SectionPos.blockToSectionCoord(BlockPos.getZ(pos));
|
|
- BlockGetter blockGetter = this.getChunk(i, j);
|
|
- if (blockGetter == null) {
|
|
- if (mutableInt != null) {
|
|
- mutableInt.setValue(16);
|
|
- }
|
|
-
|
|
- return Blocks.BEDROCK.defaultBlockState();
|
|
- } else {
|
|
- this.pos.set(pos);
|
|
- BlockState blockState = blockGetter.getBlockState(this.pos);
|
|
- boolean bl = blockState.canOcclude() && blockState.useShapeForLightOcclusion();
|
|
- if (mutableInt != null) {
|
|
- mutableInt.setValue(blockState.getLightBlock(this.chunkSource.getLevel(), this.pos));
|
|
- }
|
|
-
|
|
- return bl ? blockState : Blocks.AIR.defaultBlockState();
|
|
- }
|
|
- }
|
|
- }
|
|
+ // Paper start - comment out, see getBlockOptimized
|
|
+ // protected BlockState getStateAndOpacity(long pos, @Nullable MutableInt mutableInt) {
|
|
+ // if (pos == Long.MAX_VALUE) {
|
|
+ // if (mutableInt != null) {
|
|
+ // mutableInt.setValue(0);
|
|
+ // }
|
|
+ //
|
|
+ // return Blocks.AIR.defaultBlockState();
|
|
+ // } else {
|
|
+ // int i = SectionPos.blockToSectionCoord(BlockPos.getX(pos));
|
|
+ // int j = SectionPos.blockToSectionCoord(BlockPos.getZ(pos));
|
|
+ // BlockGetter blockGetter = this.getChunk(i, j);
|
|
+ // if (blockGetter == null) {
|
|
+ // if (mutableInt != null) {
|
|
+ // mutableInt.setValue(16);
|
|
+ // }
|
|
+ //
|
|
+ // return Blocks.BEDROCK.defaultBlockState();
|
|
+ // } else {
|
|
+ // this.pos.set(pos);
|
|
+ // BlockState blockState = blockGetter.getBlockState(this.pos);
|
|
+ // boolean bl = blockState.canOcclude() && blockState.useShapeForLightOcclusion();
|
|
+ // if (mutableInt != null) {
|
|
+ // mutableInt.setValue(blockState.getLightBlock(this.chunkSource.getLevel(), this.pos));
|
|
+ // }
|
|
+ //
|
|
+ // return bl ? blockState : Blocks.AIR.defaultBlockState();
|
|
+ // }
|
|
+ // }
|
|
+ // }
|
|
+ // Paper end
|
|
|
|
protected VoxelShape getShape(BlockState world, long pos, Direction facing) {
|
|
return world.canOcclude() ? world.getFaceOcclusionShape(this.chunkSource.getLevel(), this.pos.set(pos), facing) : Shapes.empty();
|
|
@@ -133,8 +163,9 @@ public abstract class LayerLightEngine<M extends DataLayerStorageMap<M>, S exten
|
|
return id == Long.MAX_VALUE ? 0 : 15 - this.storage.getStoredLevel(id);
|
|
}
|
|
|
|
+ protected int getLevel(DataLayer section, int x, int y, int z) { return 15 - section.get(x & 15, y & 15, z & 15); } // Paper - x/y/z version of below
|
|
protected int getLevel(DataLayer section, long blockPos) {
|
|
- return 15 - section.get(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)));
|
|
+ return 15 - section.get((int) (blockPos >> 38) & 15, (int) ((blockPos << 52) >> 52) & 15, (int) ((blockPos << 26) >> 38) & 15); // Paper
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java b/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java
|
|
index fd1cdb6e2023713f947b9497c605cf6f4bae8994..4db3264c45736faf92d560c7d18d892c1200b15d 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java
|
|
@@ -29,9 +29,9 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
protected final LongSet toMarkNoData = new LongOpenHashSet();
|
|
protected final LongSet toMarkData = new LongOpenHashSet();
|
|
protected volatile M e_visible; protected final Object visibleUpdateLock = new Object(); // Paper - diff on change, should be "visible" - force compile fail on usage change
|
|
- protected final M updatingSectionData;
|
|
+ protected final M updatingSectionData; protected final M updating; // Paper - diff on change, should be "updating"
|
|
protected final LongSet changedSections = new LongOpenHashSet();
|
|
- protected final LongSet sectionsAffectedByLightUpdates = new LongOpenHashSet();
|
|
+ protected final LongSet sectionsAffectedByLightUpdates = new LongOpenHashSet(); LongSet dirty = sectionsAffectedByLightUpdates; // Paper - OBFHELPER
|
|
protected final Long2ObjectMap<DataLayer> queuedSections = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
|
private final LongSet untrustedSections = new LongOpenHashSet();
|
|
private final LongSet columnsToRetainQueuedDataFor = new LongOpenHashSet();
|
|
@@ -39,33 +39,33 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
protected volatile boolean hasToRemove;
|
|
|
|
protected LayerLightSectionStorage(LightLayer lightType, LightChunkGetter chunkProvider, M lightData) {
|
|
- super(3, 16, 256);
|
|
+ super(3, 256, 256); // Paper - bump expected size of level sets to improve collisions and reduce rehashing (seen a lot of it)
|
|
this.layer = lightType;
|
|
this.chunkSource = chunkProvider;
|
|
- this.updatingSectionData = lightData;
|
|
+ this.updatingSectionData = lightData; updating = lightData; // Paper
|
|
this.e_visible = lightData.copy(); // Paper - avoid copying light dat
|
|
this.e_visible.disableCache(); // Paper - avoid copying light dat
|
|
}
|
|
|
|
- protected boolean storingLightForSection(long sectionPos) {
|
|
- return this.getDataLayer(sectionPos, true) != null;
|
|
+ protected final boolean storingLightForSection(long sectionPos) { // Paper - final to help inlining
|
|
+ return this.updating.getUpdatingOptimized(sectionPos) != null; // Paper - inline to avoid branching
|
|
}
|
|
|
|
@Nullable
|
|
protected DataLayer getDataLayer(long sectionPos, boolean cached) {
|
|
// Paper start - avoid copying light data
|
|
if (cached) {
|
|
- return this.getDataLayer(this.updatingSectionData, sectionPos);
|
|
+ return this.updating.getUpdatingOptimized(sectionPos);
|
|
} else {
|
|
synchronized (this.visibleUpdateLock) {
|
|
- return this.getDataLayer(this.e_visible, sectionPos);
|
|
+ return this.e_visible.lookup.apply(sectionPos);
|
|
}
|
|
}
|
|
// Paper end - avoid copying light data
|
|
}
|
|
|
|
@Nullable
|
|
- protected DataLayer getDataLayer(M storage, long sectionPos) {
|
|
+ protected final DataLayer getDataLayer(M storage, long sectionPos) { // Paper
|
|
return storage.getLayer(sectionPos);
|
|
}
|
|
|
|
@@ -78,24 +78,56 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
protected abstract int getLightValue(long blockPos);
|
|
|
|
protected int getStoredLevel(long blockPos) {
|
|
- long l = SectionPos.blockToSection(blockPos);
|
|
- DataLayer dataLayer = this.getDataLayer(l, true);
|
|
- return dataLayer.get(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)));
|
|
+ // Paper start - reuse and inline math, use Optimized Updating path
|
|
+ final int x = (int) (blockPos >> 38);
|
|
+ final int y = (int) ((blockPos << 52) >> 52);
|
|
+ final int z = (int) ((blockPos << 26) >> 38);
|
|
+ long j = SectionPos.blockPosAsSectionLong(x, y, z);
|
|
+ DataLayer nibblearray = this.updating.getUpdatingOptimized(j);
|
|
+ // BUG: Sometimes returns null and crashes, try to recover, but to prevent crash just return no light.
|
|
+ if (nibblearray == null) {
|
|
+ nibblearray = this.e_visible.lookup.apply(j);
|
|
+ }
|
|
+ if (nibblearray == null) {
|
|
+ System.err.println("Null nibble, preventing crash " + BlockPos.of(blockPos));
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return nibblearray.get(x & 15, y & 15, z & 15); // Paper - inline operations
|
|
+ // Paper end
|
|
}
|
|
|
|
protected void setStoredLevel(long blockPos, int value) {
|
|
- long l = SectionPos.blockToSection(blockPos);
|
|
+ // Paper start - cache part of the math done in loop below
|
|
+ int x = (int) (blockPos >> 38);
|
|
+ int y = (int) ((blockPos << 52) >> 52);
|
|
+ int z = (int) ((blockPos << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(x, y, z);
|
|
+ // Paper end
|
|
if (this.changedSections.add(l)) {
|
|
this.updatingSectionData.copyDataLayer(l);
|
|
}
|
|
|
|
DataLayer dataLayer = this.getDataLayer(l, true);
|
|
- dataLayer.set(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)), value);
|
|
-
|
|
- for(int i = -1; i <= 1; ++i) {
|
|
- for(int j = -1; j <= 1; ++j) {
|
|
- for(int k = -1; k <= 1; ++k) {
|
|
- this.sectionsAffectedByLightUpdates.add(SectionPos.blockToSection(BlockPos.offset(blockPos, j, k, i)));
|
|
+ dataLayer.set(x & 15, y & 15, z & 15, value); // Paper - use already calculated x/y/z
|
|
+
|
|
+ // Paper start - credit to JellySquid for a major optimization here:
|
|
+ /*
|
|
+ * An extremely important optimization is made here in regards to adding items to the pending notification set. The
|
|
+ * original implementation attempts to add the coordinate of every chunk which contains a neighboring block position
|
|
+ * even though a huge number of loop iterations will simply map to block positions within the same updating chunk.
|
|
+ *
|
|
+ * Our implementation here avoids this by pre-calculating the min/max chunk coordinates so we can iterate over only
|
|
+ * the relevant chunk positions once. This reduces what would always be 27 iterations to just 1-8 iterations.
|
|
+ *
|
|
+ * @reason Use faster implementation
|
|
+ * @author JellySquid
|
|
+ */
|
|
+ for (int z2 = (z - 1) >> 4; z2 <= (z + 1) >> 4; ++z2) {
|
|
+ for (int x2 = (x - 1) >> 4; x2 <= (x + 1) >> 4; ++x2) {
|
|
+ for (int y2 = (y - 1) >> 4; y2 <= (y + 1) >> 4; ++y2) {
|
|
+ this.dirty.add(SectionPos.asLong(x2, y2, z2));
|
|
+ // Paper end
|
|
}
|
|
}
|
|
}
|
|
@@ -136,17 +168,23 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
}
|
|
|
|
if (i >= 2 && level != 2) {
|
|
- if (this.toRemove.contains(id)) {
|
|
- this.toRemove.remove(id);
|
|
- } else {
|
|
+ if (!this.toRemove.remove(id)) { // Paper - remove useless contains - credit to JellySquid
|
|
+ //this.toRemove.remove(id); // Paper
|
|
+ //} else { // Paper
|
|
this.updatingSectionData.setLayer(id, this.createDataLayer(id));
|
|
this.changedSections.add(id);
|
|
this.onNodeAdded(id);
|
|
|
|
- for(int j = -1; j <= 1; ++j) {
|
|
- for(int k = -1; k <= 1; ++k) {
|
|
- for(int l = -1; l <= 1; ++l) {
|
|
- this.sectionsAffectedByLightUpdates.add(SectionPos.blockToSection(BlockPos.offset(id, k, l, j)));
|
|
+ // Paper start - reuse x/y/z and only notify valid chunks - Credit to JellySquid (See above method for notes)
|
|
+ int x = (int) (id >> 38);
|
|
+ int y = (int) ((id << 52) >> 52);
|
|
+ int z = (int) ((id << 26) >> 38);
|
|
+
|
|
+ for (int z2 = (z - 1) >> 4; z2 <= (z + 1) >> 4; ++z2) {
|
|
+ for (int x2 = (x - 1) >> 4; x2 <= (x + 1) >> 4; ++x2) {
|
|
+ for (int y2 = (y - 1) >> 4; y2 <= (y + 1) >> 4; ++y2) {
|
|
+ this.dirty.add(SectionPos.asLong(x2, y2, z2));
|
|
+ // Paper end
|
|
}
|
|
}
|
|
}
|
|
@@ -171,9 +209,9 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
return SectionPos.blockToSection(mx) == sectionPos;
|
|
});
|
|
} else {
|
|
- int i = SectionPos.sectionToBlockCoord(SectionPos.x(sectionPos));
|
|
- int j = SectionPos.sectionToBlockCoord(SectionPos.y(sectionPos));
|
|
- int k = SectionPos.sectionToBlockCoord(SectionPos.z(sectionPos));
|
|
+ int i = (int) (sectionPos >> 42) << 4; // Paper - inline
|
|
+ int j = (int) (sectionPos << 44 >> 44) << 4; // Paper - inline
|
|
+ int k = (int) (sectionPos << 22 >> 42) << 4; // Paper - inline
|
|
|
|
for(int l = 0; l < 16; ++l) {
|
|
for(int m = 0; m < 16; ++m) {
|
|
@@ -215,11 +253,12 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
this.toRemove.clear();
|
|
this.hasToRemove = false;
|
|
|
|
+ DataLayer test = null; // Paper
|
|
for(Entry<DataLayer> entry : this.queuedSections.long2ObjectEntrySet()) {
|
|
long n = entry.getLongKey();
|
|
- if (this.storingLightForSection(n)) {
|
|
+ if ((test = this.updating.getUpdatingOptimized(n)) != null) { // Paper - dont look up data layer twice
|
|
DataLayer dataLayer3 = entry.getValue();
|
|
- if (this.updatingSectionData.getLayer(n) != dataLayer3) {
|
|
+ if (test != dataLayer3) { // Paper
|
|
this.clearQueuedSectionBlocks(lightProvider, n);
|
|
this.updatingSectionData.setLayer(n, dataLayer3);
|
|
this.changedSections.add(n);
|
|
@@ -254,12 +293,17 @@ public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>>
|
|
|
|
private void checkEdgesForSection(LayerLightEngine<M, ?> lightProvider, long sectionPos) {
|
|
if (this.storingLightForSection(sectionPos)) {
|
|
- int i = SectionPos.sectionToBlockCoord(SectionPos.x(sectionPos));
|
|
- int j = SectionPos.sectionToBlockCoord(SectionPos.y(sectionPos));
|
|
- int k = SectionPos.sectionToBlockCoord(SectionPos.z(sectionPos));
|
|
+ // Paper start
|
|
+ int secX = (int) (sectionPos >> 42);
|
|
+ int secY = (int) (sectionPos << 44 >> 44);
|
|
+ int secZ = (int) (sectionPos << 22 >> 42);
|
|
+ int i = secX << 4; // baseX
|
|
+ int j = secY << 4; // baseY
|
|
+ int k = secZ << 4; // baseZ
|
|
+ // Paper end
|
|
|
|
for(Direction direction : DIRECTIONS) {
|
|
- long l = SectionPos.offset(sectionPos, direction);
|
|
+ long l = SectionPos.getAdjacentFromSectionPos(secX, secY, secZ, direction);
|
|
if (!this.queuedSections.containsKey(l) && this.storingLightForSection(l)) {
|
|
for(int m = 0; m < 16; ++m) {
|
|
for(int n = 0; n < 16; ++n) {
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java
|
|
index d122475c1a9d340046c478087d3ff5bf1ff8932c..a3189dd7b537deb4a30a32c8e9891b07d04af540 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java
|
|
@@ -4,6 +4,7 @@ import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.world.level.LightLayer;
|
|
+import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.DataLayer;
|
|
import net.minecraft.world.level.chunk.LightChunkGetter;
|
|
@@ -27,32 +28,37 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
return level;
|
|
} else {
|
|
//MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded
|
|
- BlockState blockState = this.getStateAndOpacity(targetId, mutableInt);
|
|
- if (mutableInt.getValue() >= 15) {
|
|
+ // Paper start - use x/y/z and optimized block lookup
|
|
+ int jx = (int) (targetId >> 38);
|
|
+ int jy = (int) ((targetId << 52) >> 52);
|
|
+ int jz = (int) ((targetId << 26) >> 38);
|
|
+ BlockState blockState = this.getBlockOptimized(jx, jy, jz, mutableInt);
|
|
+ int blockedLight = mutableInt.getValue();
|
|
+ if (blockedLight >= 15) {
|
|
+ // Paper end
|
|
return 15;
|
|
} else {
|
|
- int i = BlockPos.getX(sourceId);
|
|
- int j = BlockPos.getY(sourceId);
|
|
- int k = BlockPos.getZ(sourceId);
|
|
- int l = BlockPos.getX(targetId);
|
|
- int m = BlockPos.getY(targetId);
|
|
- int n = BlockPos.getZ(targetId);
|
|
- int o = Integer.signum(l - i);
|
|
- int p = Integer.signum(m - j);
|
|
- int q = Integer.signum(n - k);
|
|
+ // Paper start - inline math
|
|
+ int i = (int) (sourceId >> 38);
|
|
+ int j = (int) ((sourceId << 52) >> 52);
|
|
+ int k = (int) ((sourceId << 26) >> 38);
|
|
+ boolean bl = i == jx && k == jz;
|
|
+ int o = Integer.signum(jx - i);
|
|
+ int p = Integer.signum(jy - j);
|
|
+ int q = Integer.signum(jz - k);
|
|
+ // Paper end
|
|
Direction direction = Direction.fromNormal(o, p, q);
|
|
if (direction == null) {
|
|
throw new IllegalStateException(String.format("Light was spread in illegal direction %d, %d, %d", o, p, q));
|
|
} else {
|
|
- BlockState blockState2 = this.getStateAndOpacity(sourceId, (MutableInt)null);
|
|
+ BlockState blockState2 = sourceId == Long.MAX_VALUE ? net.minecraft.world.level.block.Blocks.AIR.defaultBlockState() : this.getBlockOptimized(i, j, k); // Paper
|
|
VoxelShape voxelShape = this.getShape(blockState2, sourceId, direction);
|
|
VoxelShape voxelShape2 = this.getShape(blockState, targetId, direction.getOpposite());
|
|
if (Shapes.faceShapeOccludes(voxelShape, voxelShape2)) {
|
|
return 15;
|
|
} else {
|
|
- boolean bl = i == l && k == n;
|
|
- boolean bl2 = bl && j > m;
|
|
- return bl2 && level == 0 && mutableInt.getValue() == 0 ? 0 : level + Math.max(1, mutableInt.getValue());
|
|
+ boolean bl2 = bl && j > jy; // Paper rename vars to jy from m
|
|
+ return bl2 && level == 0 && blockedLight == 0 ? 0 : level + Math.max(1, blockedLight); // Paper
|
|
}
|
|
}
|
|
}
|
|
@@ -64,10 +70,14 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
|
|
@Override
|
|
protected void checkNeighborsAfterUpdate(long id, int level, boolean decrease) {
|
|
- long l = SectionPos.blockToSection(id);
|
|
- int i = BlockPos.getY(id);
|
|
- int j = SectionPos.sectionRelative(i);
|
|
- int k = SectionPos.blockToSectionCoord(i);
|
|
+ // Paper start
|
|
+ int baseX = (int) (id >> 38);
|
|
+ int baseY = (int) ((id << 52) >> 52);
|
|
+ int baseZ = (int) ((id << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ int j = baseY & 15;
|
|
+ int k = baseY >> 4;
|
|
+ // Paper end
|
|
int m;
|
|
if (j != 0) {
|
|
m = 0;
|
|
@@ -79,14 +89,15 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
m = n;
|
|
}
|
|
|
|
- long p = BlockPos.offset(id, 0, -1 - m * 16, 0);
|
|
- long q = SectionPos.blockToSection(p);
|
|
+ int newBaseY = baseY + (-1 - m * 16); // Paper
|
|
+ long p = BlockPos.asLong(baseX, newBaseY, baseZ); // Paper
|
|
+ long q = SectionPos.blockPosAsSectionLong(baseX, newBaseY, baseZ); // Paper
|
|
if (l == q || this.storage.storingLightForSection(q)) {
|
|
this.checkNeighbor(id, p, level, decrease);
|
|
}
|
|
|
|
- long r = BlockPos.offset(id, Direction.UP);
|
|
- long s = SectionPos.blockToSection(r);
|
|
+ long r = BlockPos.asLong(baseX, baseY + 1, baseZ); // Paper
|
|
+ long s = SectionPos.blockPosAsSectionLong(baseX, baseY + 1, baseZ); // Paper
|
|
if (l == s || this.storage.storingLightForSection(s)) {
|
|
this.checkNeighbor(id, r, level, decrease);
|
|
}
|
|
@@ -95,8 +106,8 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
int t = 0;
|
|
|
|
while(true) {
|
|
- long u = BlockPos.offset(id, direction.getStepX(), -t, direction.getStepZ());
|
|
- long v = SectionPos.blockToSection(u);
|
|
+ long u = BlockPos.asLong(baseX + direction.getStepX(), baseY - t, baseZ + direction.getStepZ()); // Paper
|
|
+ long v = SectionPos.blockPosAsSectionLong(baseX + direction.getStepX(), baseY - t, baseZ + direction.getStepZ()); // Paper
|
|
if (l == v) {
|
|
this.checkNeighbor(id, u, level, decrease);
|
|
break;
|
|
@@ -119,23 +130,33 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
|
@Override
|
|
protected int getComputedLevel(long id, long excludedId, int maxLevel) {
|
|
int i = maxLevel;
|
|
- long l = SectionPos.blockToSection(id);
|
|
- DataLayer dataLayer = this.storage.getDataLayer(l, true);
|
|
+ // Paper start
|
|
+ int baseX = (int) (id >> 38);
|
|
+ int baseY = (int) ((id << 52) >> 52);
|
|
+ int baseZ = (int) ((id << 26) >> 38);
|
|
+ long l = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ DataLayer dataLayer = this.storage.updating.getUpdatingOptimized(l);
|
|
+ // Paper end
|
|
|
|
for(Direction direction : DIRECTIONS) {
|
|
- long m = BlockPos.offset(id, direction);
|
|
+ // Paper start
|
|
+ int newX = baseX + direction.getStepX();
|
|
+ int newY = baseY + direction.getStepY();
|
|
+ int newZ = baseZ + direction.getStepZ();
|
|
+ long m = BlockPos.asLong(newX, newY, newZ);
|
|
if (m != excludedId) {
|
|
- long n = SectionPos.blockToSection(m);
|
|
+ long n = SectionPos.blockPosAsSectionLong(newX, newY, newZ);
|
|
+ // Paper end
|
|
DataLayer dataLayer2;
|
|
if (l == n) {
|
|
dataLayer2 = dataLayer;
|
|
} else {
|
|
- dataLayer2 = this.storage.getDataLayer(n, true);
|
|
+ dataLayer2 = this.storage.updating.getUpdatingOptimized(n); // Paper
|
|
}
|
|
|
|
int j;
|
|
if (dataLayer2 != null) {
|
|
- j = this.getLevel(dataLayer2, m);
|
|
+ j = this.getLevel(dataLayer2, newX, newY, newZ); // Paper
|
|
} else {
|
|
if (direction == Direction.DOWN) {
|
|
continue;
|
|
diff --git a/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java b/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java
|
|
index ec3837a64e8ac6892028611d57a111a7fd5c58f7..006a4b02df0f7d9229b1f3791d4f3b9243919c18 100644
|
|
--- a/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java
|
|
+++ b/src/main/java/net/minecraft/world/level/lighting/SkyLightSectionStorage.java
|
|
@@ -30,7 +30,12 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
}
|
|
|
|
protected int getLightValue(long l, boolean bl) {
|
|
- long m = SectionPos.blockToSection(l);
|
|
+ // Paper start
|
|
+ int baseX = (int) (l >> 38);
|
|
+ int baseY = (int) ((l << 52) >> 52);
|
|
+ int baseZ = (int) ((l << 26) >> 38);
|
|
+ long m = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ);
|
|
+ // Paper end
|
|
int i = SectionPos.y(m);
|
|
synchronized (this.visibleUpdateLock) { // Paper - avoid copying light data
|
|
SkyLightSectionStorage.SkyDataLayerStorageMap skyDataLayerStorageMap = (SkyLightSectionStorage.SkyDataLayerStorageMap) this.e_visible; // Paper - avoid copying light data - must be after lock acquire
|
|
@@ -49,7 +54,7 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
}
|
|
}
|
|
|
|
- return dataLayer.get(SectionPos.sectionRelative(BlockPos.getX(l)), SectionPos.sectionRelative(BlockPos.getY(l)), SectionPos.sectionRelative(BlockPos.getZ(l)));
|
|
+ return dataLayer.get(baseX & 15, (int) ((l << 52) >> 52) & 15, (int) baseZ & 15);
|
|
} else {
|
|
return bl && !this.lightOnInSection(m) ? 0 : 15;
|
|
}
|
|
@@ -157,7 +162,7 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
int i = (this.updatingSectionData).otherData.getUpdating(SectionPos.getZeroNode(sectionPos)); // Paper - avoid copying light data
|
|
if (i != (this.updatingSectionData).currentLowestY && SectionPos.y(l) < i) {
|
|
DataLayer dataLayer2;
|
|
- while((dataLayer2 = this.getDataLayer(l, true)) == null) {
|
|
+ while((dataLayer2 = this.updating.getUpdatingOptimized(l)) == null) { // Paper
|
|
l = SectionPos.offset(l, Direction.UP);
|
|
}
|
|
|
|
@@ -189,6 +194,9 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
if (doSkylight) {
|
|
if (!this.sectionsToAddSourcesTo.isEmpty()) {
|
|
for(long l : this.sectionsToAddSourcesTo) {
|
|
+ int baseX = (int) (l >> 42) << 4; // Paper
|
|
+ int baseY = (int) (l << 44 >> 44) << 4; // Paper
|
|
+ int baseZ = (int) (l << 22 >> 42) << 4; // Paper
|
|
int i = this.getLevel(l);
|
|
if (i != 2 && !this.sectionsToRemoveSourcesFrom.contains(l) && this.sectionsWithSources.add(l)) {
|
|
if (i == 1) {
|
|
@@ -197,13 +205,15 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
this.updatingSectionData.copyDataLayer(l);
|
|
}
|
|
|
|
- Arrays.fill(this.getDataLayer(l, true).asBytesPoolSafe(), (byte)-1); // Paper
|
|
- int j = SectionPos.sectionToBlockCoord(SectionPos.x(l));
|
|
- int k = SectionPos.sectionToBlockCoord(SectionPos.y(l));
|
|
- int m = SectionPos.sectionToBlockCoord(SectionPos.z(l));
|
|
+ // Paper start
|
|
+ Arrays.fill(this.updating.getUpdatingOptimized(l).asBytesPoolSafe(), (byte)-1); // Paper - use optimized
|
|
+ int j = baseX;
|
|
+ int k = baseY;
|
|
+ int m = baseZ;
|
|
+ // Paper end
|
|
|
|
for(Direction direction : HORIZONTALS) {
|
|
- long n = SectionPos.offset(l, direction);
|
|
+ long n = SectionPos.getAdjacentFromBlockPos(baseX, baseY, baseZ, direction); // Paper
|
|
if ((this.sectionsToRemoveSourcesFrom.contains(n) || !this.sectionsWithSources.contains(n) && !this.sectionsToAddSourcesTo.contains(n)) && this.storingLightForSection(n)) {
|
|
for(int o = 0; o < 16; ++o) {
|
|
for(int p = 0; p < 16; ++p) {
|
|
@@ -235,15 +245,15 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
|
|
for(int y = 0; y < 16; ++y) {
|
|
for(int z = 0; z < 16; ++z) {
|
|
- long aa = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(l), y), SectionPos.sectionToBlockCoord(SectionPos.y(l)), SectionPos.sectionToBlockCoord(SectionPos.z(l), z));
|
|
- long ab = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(l), y), SectionPos.sectionToBlockCoord(SectionPos.y(l)) - 1, SectionPos.sectionToBlockCoord(SectionPos.z(l), z));
|
|
+ long aa = BlockPos.asLong(baseX + y, baseY, baseZ + z); // Paper
|
|
+ long ab = BlockPos.asLong(baseX + y, baseY - 1, baseZ + z); // Paper
|
|
lightProvider.checkEdge(aa, ab, lightProvider.computeLevelFromNeighbor(aa, ab, 0), true);
|
|
}
|
|
}
|
|
} else {
|
|
for(int ac = 0; ac < 16; ++ac) {
|
|
for(int ad = 0; ad < 16; ++ad) {
|
|
- long ae = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(l), ac), SectionPos.sectionToBlockCoord(SectionPos.y(l), 15), SectionPos.sectionToBlockCoord(SectionPos.z(l), ad));
|
|
+ long ae = BlockPos.asLong(baseX + ac, baseY + 16 - 1, baseZ + ad); // Paper
|
|
lightProvider.checkEdge(Long.MAX_VALUE, ae, 0, true);
|
|
}
|
|
}
|
|
@@ -255,10 +265,13 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage<SkyLightSec
|
|
this.sectionsToAddSourcesTo.clear();
|
|
if (!this.sectionsToRemoveSourcesFrom.isEmpty()) {
|
|
for(long af : this.sectionsToRemoveSourcesFrom) {
|
|
+ int baseX = (int) (af >> 42) << 4; // Paper
|
|
+ int baseY = (int) (af << 44 >> 44) << 4; // Paper
|
|
+ int baseZ = (int) (af << 22 >> 42) << 4; // Paper
|
|
if (this.sectionsWithSources.remove(af) && this.storingLightForSection(af)) {
|
|
for(int ag = 0; ag < 16; ++ag) {
|
|
for(int ah = 0; ah < 16; ++ah) {
|
|
- long ai = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(af), ag), SectionPos.sectionToBlockCoord(SectionPos.y(af), 15), SectionPos.sectionToBlockCoord(SectionPos.z(af), ah));
|
|
+ long ai = BlockPos.asLong(baseX + ag, baseY + 16 - 1, baseZ + ah); // Paper
|
|
lightProvider.checkEdge(Long.MAX_VALUE, ai, 15, false);
|
|
}
|
|
}
|