diff --git a/patches/api-unmapped/Add-and-implement-PlayerRecipeBookClickEvent.patch b/patches/api/Add-and-implement-PlayerRecipeBookClickEvent.patch similarity index 100% rename from patches/api-unmapped/Add-and-implement-PlayerRecipeBookClickEvent.patch rename to patches/api/Add-and-implement-PlayerRecipeBookClickEvent.patch diff --git a/patches/api-unmapped/Expose-Arrow-getItemStack.patch b/patches/api/Expose-Arrow-getItemStack.patch similarity index 100% rename from patches/api-unmapped/Expose-Arrow-getItemStack.patch rename to patches/api/Expose-Arrow-getItemStack.patch diff --git a/patches/api-unmapped/Inventory-getHolder-method-without-block-snapshot.patch b/patches/api/Inventory-getHolder-method-without-block-snapshot.patch similarity index 100% rename from patches/api-unmapped/Inventory-getHolder-method-without-block-snapshot.patch rename to patches/api/Inventory-getHolder-method-without-block-snapshot.patch diff --git a/patches/server-remapped/Ensure-Entity-AABB-s-are-never-invalid.patch b/patches/server-remapped/Ensure-Entity-AABB-s-are-never-invalid.patch deleted file mode 100644 index 1bc8b9589d..0000000000 --- a/patches/server-remapped/Ensure-Entity-AABB-s-are-never-invalid.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Sun, 10 May 2020 22:12:46 -0400 -Subject: [PATCH] Ensure Entity AABB's are never invalid - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -0,0 +0,0 @@ import net.minecraft.world.Nameable; - import net.minecraft.world.damagesource.DamageSource; - import net.minecraft.world.entity.animal.AbstractFish; - import net.minecraft.world.entity.animal.Animal; -+import net.minecraft.world.entity.decoration.HangingEntity; - import net.minecraft.world.entity.item.ItemEntity; - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.entity.vehicle.AbstractMinecart; -@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s - - public void setPos(double x, double y, double z) { - this.setPosRaw(x, y, z); -- this.setBoundingBox(this.dimensions.makeBoundingBox(x, y, z)); -+ //this.a(this.size.a(d0, d1, d2)); // Paper - move into setPositionRaw - if (valid) ((ServerLevel) level).updateChunkPos(this); // CraftBukkit - } - -@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s - return new AABB(vec3d, vec3d1); - } - -+ public final void setBoundingBox(AABB axisalignedbb) { setBoundingBox(axisalignedbb); } // Paper - OBFHELPER - public void setBoundingBox(AABB boundingBox) { - // CraftBukkit start - block invalid bounding boxes - double minX = boundingBox.minX, -@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s - } - - public void setPosRaw(double x, double y, double z) { -+ // Paper start - never allow AABB to become desynced from position -+ // hanging has its own special logic -+ if (!(this instanceof HangingEntity) && (this.position.x != x || this.position.y != y || this.position.z != z)) { -+ this.setBoundingBox(this.dimensions.makeBoundingBox(x, y, z)); -+ } -+ // Paper end - if (this.position.x != x || this.position.y != y || this.position.z != z) { - this.position = new Vec3(x, y, z); - int i = Mth.floor(x); diff --git a/patches/server-remapped/Fix-enderdragon-exp-dupe.patch b/patches/server-remapped/Fix-enderdragon-exp-dupe.patch deleted file mode 100644 index 90d2373fce..0000000000 --- a/patches/server-remapped/Fix-enderdragon-exp-dupe.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Fri, 12 Jun 2020 22:25:11 -0700 -Subject: [PATCH] Fix enderdragon exp dupe - -Properly track death stage when unloading/loading in the -dragon - -diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java -@@ -0,0 +0,0 @@ public class EnderDragon extends Mob implements Enemy { - public void addAdditionalSaveData(CompoundTag tag) { - super.addAdditionalSaveData(tag); - tag.putInt("DragonPhase", this.phaseManager.getCurrentPhase().getPhase().getId()); -+ tag.putInt("Paper.DeathTick", this.dragonDeathTime); // Paper - } - - @Override -@@ -0,0 +0,0 @@ public class EnderDragon extends Mob implements Enemy { - if (tag.contains("DragonPhase")) { - this.phaseManager.setPhase(EnderDragonPhase.getById(tag.getInt("DragonPhase"))); - } -+ this.dragonDeathTime = tag.getInt("Paper.DeathTick"); // Paper - - } - diff --git a/patches/server-remapped/Limit-lightning-strike-effect-distance.patch b/patches/server-remapped/Limit-lightning-strike-effect-distance.patch deleted file mode 100644 index 49af9e462e..0000000000 --- a/patches/server-remapped/Limit-lightning-strike-effect-distance.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Trigary -Date: Fri, 14 Sep 2018 17:42:08 +0200 -Subject: [PATCH] Limit lightning strike effect distance - - -diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -0,0 +0,0 @@ public class PaperWorldConfig { - delayChunkUnloadsBy *= 20; - } - } -+ -+ public double sqrMaxThunderDistance; -+ public double sqrMaxLightningImpactSoundDistance; -+ public double maxLightningFlashDistance; -+ private void lightningStrikeDistanceLimit() { -+ sqrMaxThunderDistance = getInt("lightning-strike-distance-limit.sound", -1); -+ if (sqrMaxThunderDistance > 0) { -+ sqrMaxThunderDistance *= sqrMaxThunderDistance; -+ } -+ -+ sqrMaxLightningImpactSoundDistance = getInt("lightning-strike-distance-limit.impact-sound", -1); -+ if (sqrMaxLightningImpactSoundDistance < 0) { -+ sqrMaxLightningImpactSoundDistance = 32 * 32; //Vanilla value -+ } else { -+ sqrMaxLightningImpactSoundDistance *= sqrMaxLightningImpactSoundDistance; -+ } -+ -+ maxLightningFlashDistance = getInt("lightning-strike-distance-limit.flash", -1); -+ if (maxLightningFlashDistance < 0) { -+ maxLightningFlashDistance = 512; // Vanilla value -+ } -+ } - } -diff --git a/src/main/java/net/minecraft/world/entity/LightningBolt.java b/src/main/java/net/minecraft/world/entity/LightningBolt.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/LightningBolt.java -+++ b/src/main/java/net/minecraft/world/entity/LightningBolt.java -@@ -0,0 +0,0 @@ import net.minecraft.server.level.ServerPlayer; - import net.minecraft.sounds.SoundEvents; - import net.minecraft.sounds.SoundSource; - import net.minecraft.world.Difficulty; --import net.minecraft.world.entity.player.Player; - import net.minecraft.world.level.BlockGetter; - import net.minecraft.world.level.GameRules; - import net.minecraft.world.level.Level; -@@ -0,0 +0,0 @@ public class LightningBolt extends Entity { - double deltaX = this.getX() - player.getX(); - double deltaZ = this.getZ() - player.getZ(); - double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; -+ // Paper start - Limit lightning strike effect distance -+ if (distanceSquared <= this.level.paperConfig.sqrMaxLightningImpactSoundDistance) { -+ player.connection.send(new ClientboundSoundPacket(SoundEvents.LIGHTNING_BOLT_IMPACT, -+ SoundSource.WEATHER, this.getX(), this.getY(), this.getZ(), 2.0f, 0.5F + this.random.nextFloat() * 0.2F)); -+ } -+ -+ if (level.paperConfig.sqrMaxThunderDistance != -1 && distanceSquared >= level.paperConfig.sqrMaxThunderDistance) { -+ continue; -+ } -+ -+ // Paper end - if (distanceSquared > viewDistance * viewDistance) { - double deltaLength = Math.sqrt(distanceSquared); - double relativeX = player.getX() + (deltaX / deltaLength) * viewDistance; -@@ -0,0 +0,0 @@ public class LightningBolt extends Entity { - } - } - // CraftBukkit end -- this.level.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.LIGHTNING_BOLT_IMPACT, SoundSource.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F); -+// this.world.playSound((EntityHuman) null, this.locX(), this.locY(), this.locZ(), SoundEffects.ENTITY_LIGHTNING_BOLT_IMPACT, SoundCategory.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F); // Paper - Limit lightning strike effect distance (the packet is now sent from inside the loop) - } - - --this.life; diff --git a/patches/server-remapped/Optimize-Light-Engine.patch b/patches/server-remapped/Optimize-Light-Engine.patch deleted file mode 100644 index 0581e181ce..0000000000 --- a/patches/server-remapped/Optimize-Light-Engine.patch +++ /dev/null @@ -1,1464 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java -+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -0,0 +0,0 @@ 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(), getCurrentPriority(), priority); - } - if (getCurrentPriority() != priority) { - this.onLevelChange.onLevelChange(this.pos, this::getCurrentPriority, priority, this::setPriority); // 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -0,0 +0,0 @@ import net.minecraft.world.level.levelgen.structure.StructureStart; - import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelStorageSource; -+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; -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - // Paper end - -+ private final java.util.concurrent.ExecutorService lightThread; - public ChunkMap(ServerLevel worldserver, LevelStorageSource.LevelStorageAccess convertable_conversionsession, DataFixer dataFixer, StructureManager definedstructuremanager, Executor workerExecutor, BlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, Supplier supplier, int i, boolean flag) { - super(new File(convertable_conversionsession.getDimensionPath(worldserver.dimension()), "region"), dataFixer, flag); - //this.visibleChunks = this.updatingChunks.clone(); // Paper - no more cloning -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - ProcessorHandle mailbox = ProcessorHandle.of("main", mainThreadExecutor::tell); - - this.progressListener = worldGenerationProgressListener; -- ProcessorMailbox lightthreaded; ProcessorMailbox threadedmailbox1 = lightthreaded = ProcessorMailbox.create(workerExecutor, "light"); // Paper -+ // Paper start - use light thread -+ ProcessorMailbox lightthreaded; ProcessorMailbox 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), workerExecutor, Integer.MAX_VALUE); - this.worldgenMailbox = this.queueSorter.getProcessor(threadedmailbox, false); -@@ -0,0 +0,0 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper end - } - -+ protected final IntSupplier getPrioritySupplier(long i) { return getChunkQueueLevel(i); } // Paper - OBFHELPER - protected IntSupplier getChunkQueueLevel(long pos) { - return () -> { - ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos); -@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/SectionTracker.java -+++ b/src/main/java/net/minecraft/server/level/SectionTracker.java -@@ -0,0 +0,0 @@ - package net.minecraft.server.level; - --import net.minecraft.core.SectionPos; - import net.minecraft.world.level.lighting.DynamicGraphMinFixedPoint; - - public abstract class SectionTracker extends DynamicGraphMinFixedPoint { -@@ -0,0 +0,0 @@ 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 k = -1; k <= 1; ++k) { - for (int l = -1; l <= 1; ++l) { - for (int i1 = -1; i1 <= 1; ++i1) { -- long j1 = SectionPos.offset(id, k, l, i1); -+ if (k == 0 && l == 0 && i1 == 0) continue; // Paper -+ long j1 = (((long) (x + k) & 4194303L) << 42) | (((long) (y + l) & 1048575L)) | (((long) (z + i1) & 4194303L) << 20); // Paper - -- if (j1 != id) { -+ //if (j1 != i) { // Paper - checked above - this.checkNeighbor(id, j1, level, decrease); -- } -+ //} // Paper - } - } - } -@@ -0,0 +0,0 @@ public abstract class SectionTracker extends DynamicGraphMinFixedPoint { - protected int getComputedLevel(long id, long excludedId, int maxLevel) { - int l = maxLevel; - -+ // Paper start -+ int x = (int) (id >> 42); -+ int y = (int) (id << 44 >> 44); -+ int z = (int) (id << 22 >> 42); -+ // Paper end - for (int i1 = -1; i1 <= 1; ++i1) { - for (int j1 = -1; j1 <= 1; ++j1) { - for (int k1 = -1; k1 <= 1; ++k1) { -- long l1 = SectionPos.offset(id, i1, j1, k1); -+ long l1 = (((long) (x + i1) & 4194303L) << 42) | (((long) (y + j1) & 1048575L)) | (((long) (z + k1) & 4194303L) << 20); // Paper - - if (l1 == id) { - l1 = 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -+++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java -@@ -0,0 +0,0 @@ - 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; -@@ -0,0 +0,0 @@ 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; -@@ -0,0 +0,0 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - - private static final Logger LOGGER = LogManager.getLogger(); - private final ProcessorMailbox taskMailbox; -- private final ObjectList> 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.isAtLeastStatus(ChunkStatus.LIGHT); -+ } -+ -+ static class ChunkLightQueue { -+ public boolean shouldFastUpdate; -+ java.util.ArrayDeque pre = new java.util.ArrayDeque(); -+ java.util.ArrayDeque post = new java.util.ArrayDeque(); -+ -+ 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[] buckets = new Long2ObjectLinkedOpenHashMap[MAX_PRIORITIES]; -+ private final java.util.concurrent.ConcurrentLinkedQueue pendingTasks = new java.util.concurrent.ConcurrentLinkedQueue<>(); -+ private final java.util.concurrent.ConcurrentLinkedQueue 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 pre, java.util.List post) { -+ PendingLightTask pending; -+ while ((pending = pendingTasks.poll()) != null) { -+ add(pending); -+ } -+ Runnable run; -+ while ((run = priorityChanges.poll()) != null) { -+ run.run(); -+ } -+ boolean hasWork = false; -+ Long2ObjectLinkedOpenHashMap[] buckets = this.buckets; -+ int priority = 0; -+ while (priority < MAX_PRIORITIES && !isEmpty()) { -+ Long2ObjectLinkedOpenHashMap 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> sorterMailbox; - private volatile int taskPerBatch = 5; - private final AtomicBoolean scheduled = new AtomicBoolean(); - - public ThreadedLevelLightEngine(LightChunkGetter chunkProvider, ChunkMap chunkStorage, boolean hasBlockLight, ProcessorMailbox processor, ProcessorHandle> executor) { - super(chunkProvider, true, hasBlockLight); -- this.chunkMap = chunkStorage; -+ this.chunkMap = chunkStorage; this.playerChunkMap = chunkMap; // Paper - this.sorterMailbox = executor; - this.taskMailbox = processor; - } -@@ -0,0 +0,0 @@ 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 -@@ -0,0 +0,0 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - public CompletableFuture lightChunk(ChunkAccess chunk, boolean excludeBlocks) { - ChunkPos chunkcoordintpair = chunk.getPos(); - -- chunk.setLightCorrect(false); -- this.addTask(chunkcoordintpair.x, chunkcoordintpair.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> { -+ // Paper start -+ //ichunkaccess.b(false); // Don't need to disable this -+ long pair = chunkcoordintpair.toLong(); -+ CompletableFuture future = new CompletableFuture<>(); -+ IntSupplier prioritySupplier = playerChunkMap.getPrioritySupplier(pair); -+ boolean[] skippedPre = {false}; -+ this.queue.addChunk(pair, prioritySupplier, Util.name(() -> { -+ if (!isChunkLightStatus(pair)) { -+ future.complete(chunk); -+ skippedPre[0] = true; -+ return; -+ } -+ // Paper end - LevelChunkSection[] achunksection = chunk.getSections(); - - for (int i = 0; i < 16; ++i) { -@@ -0,0 +0,0 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl - }); - } - -- this.chunkMap.releaseLightTicket(chunkcoordintpair); -+ // this.d.c(chunkcoordintpair); // Paper - move into post task below - }, () -> { - return "lightChunk " + chunkcoordintpair + " " + excludeBlocks; -- })); -- return CompletableFuture.supplyAsync(() -> { -+ // Paper start - merge the 2 together -+ }), () -> { -+ this.chunkMap.releaseLightTicket(chunkcoordintpair); // Paper - release light tickets as post task to ensure they stay loaded until fully done -+ if (skippedPre[0]) return; // Paper - future's already complete - chunk.setLightCorrect(true); - super.retainData(chunkcoordintpair, false); -- return chunk; -- }, (runnable) -> { -- this.addTask(chunkcoordintpair.x, chunkcoordintpair.z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, runnable); -+ // Paper start -+ 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((() -> { // Paper - decompile error - 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 pre = new java.util.ArrayList<>(); -+ private final java.util.List post = new java.util.ArrayList<>(); - private void runUpdate() { -- int i = Math.min(this.lightTasks.size(), this.taskPerBatch); -- ObjectListIterator> objectlistiterator = this.lightTasks.iterator(); -- -- Pair pair; -- int j; -- -- for (j = 0; objectlistiterator.hasNext() && j < i; ++j) { -- pair = (Pair) objectlistiterator.next(); -- if (pair.getFirst() == ThreadedLevelLightEngine.TaskType.PRE_UPDATE) { -- ((Runnable) 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 (j = 0; objectlistiterator.hasNext() && j < i; ++j) { -- pair = (Pair) objectlistiterator.next(); -- if (pair.getFirst() == ThreadedLevelLightEngine.TaskType.POST_UPDATE) { -- ((Runnable) pair.getSecond()).run(); -- } -- -- objectlistiterator.remove(); -- } -- -+ // Paper end - } - - public void setTaskPerBatch(int taskBatchSize) { -diff --git a/src/main/java/net/minecraft/util/thread/ProcessorMailbox.java b/src/main/java/net/minecraft/util/thread/ProcessorMailbox.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/util/thread/ProcessorMailbox.java -+++ b/src/main/java/net/minecraft/util/thread/ProcessorMailbox.java -@@ -0,0 +0,0 @@ public class ProcessorMailbox implements ProcessorHandle, AutoCloseable, R - - } - -- @Override -+ -+ public final void queue(T t0) { tell(t0); } @Override // Paper - OBFHELPER - public void tell(T message) { - this.queue.push(message); - this.registerForExecution(); -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/DataLayer.java -+++ b/src/main/java/net/minecraft/world/level/chunk/DataLayer.java -@@ -0,0 +0,0 @@ import net.minecraft.server.MCUtil; - public class DataLayer { - - // 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java -+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java -@@ -0,0 +0,0 @@ public final class BlockLightEngine extends LayerLightEngine> 38); -+ int k = (int) ((blockPos << 52) >> 52); -+ int l = (int) ((blockPos << 26) >> 38); -+ // Paper end - BlockGetter iblockaccess = this.chunkSource.getChunkForLighting(j >> 4, l >> 4); - - return iblockaccess != null ? iblockaccess.getLightEmission(this.pos.set(j, k, l)) : 0; -@@ -0,0 +0,0 @@ public final class BlockLightEngine extends LayerLightEngine= 15) { - return level; - } else { -- int l = Integer.signum(BlockPos.getX(targetId) - BlockPos.getX(sourceId)); -- int i1 = Integer.signum(BlockPos.getY(targetId) - BlockPos.getY(sourceId)); -- int j1 = Integer.signum(BlockPos.getZ(targetId) - BlockPos.getZ(sourceId)); -+ // Paper start - reuse math - credit to JellySquid for idea -+ int jx = (int) (targetId >> 38); -+ int jy = (int) ((targetId << 52) >> 52); -+ int jz = (int) ((targetId << 26) >> 38); -+ int ix = (int) (sourceId >> 38); -+ int iy = (int) ((sourceId << 52) >> 52); -+ int iz = (int) ((sourceId << 26) >> 38); -+ int l = Integer.signum(jx - ix); -+ int i1 = Integer.signum(jy - iy); -+ int j1 = Integer.signum(jz - iz); -+ // Paper end - Direction enumdirection = Direction.fromNormal(l, i1, j1); - - if (enumdirection == null) { - return 15; - } else { - //MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded -- BlockState iblockdata = this.getStateAndOpacity(targetId, mutableint); -- -- if (mutableint.getValue() >= 15) { -+ BlockState iblockdata = this.getBlockOptimized(jx, jy, jz, mutableint); // Paper -+ int blockedLight = mutableint.getValue(); // Paper -+ if (blockedLight >= 15) { // Paper - return 15; - } else { -- BlockState iblockdata1 = this.getStateAndOpacity(sourceId, (MutableInt) null); -+ BlockState iblockdata1 = this.getBlockOptimized(ix, iy, iz); // Paper - VoxelShape voxelshape = this.getShape(iblockdata1, sourceId, enumdirection); - VoxelShape voxelshape1 = this.getShape(iblockdata, targetId, enumdirection.getOpposite()); - -- return Shapes.faceShapeOccludes(voxelshape, voxelshape1) ? 15 : level + Math.max(1, mutableint.getValue()); -+ return Shapes.faceShapeOccludes(voxelshape, voxelshape1) ? 15 : level + Math.max(1, blockedLight); // Paper - } - } - } -@@ -0,0 +0,0 @@ public final class BlockLightEngine extends LayerLightEngine> 38); -+ int y = (int) ((id << 52) >> 52); -+ int z = (int) ((id << 26) >> 38); -+ long k = SectionPos.blockPosAsSectionLong(x, y, z); -+ // Paper end - Direction[] aenumdirection = BlockLightEngine.DIRECTIONS; - int l = aenumdirection.length; - - for (int i1 = 0; i1 < l; ++i1) { - Direction enumdirection = aenumdirection[i1]; -- long j1 = BlockPos.offset(id, enumdirection); -- long k1 = SectionPos.blockToSection(j1); -+ long j1 = BlockPos.getAdjacent(x, y, z, enumdirection); // Paper -+ long k1 = SectionPos.blockToSection(j1); // Paper - - if (k == k1 || ((BlockLightSectionStorage) this.storage).storingLightForSection(k1)) { - this.checkNeighbor(id, j1, level, decrease); -@@ -0,0 +0,0 @@ public final class BlockLightEngine extends LayerLightEngine> 38); -+ int baseY = (int) ((id << 52) >> 52); -+ int baseZ = (int) ((id << 26) >> 38); -+ long j1 = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ); -+ DataLayer nibblearray = this.storage.updating.getUpdatingOptimized(j1); -+ // Paper end - Direction[] aenumdirection = BlockLightEngine.DIRECTIONS; - int k1 = aenumdirection.length; - - for (int l1 = 0; l1 < k1; ++l1) { - Direction enumdirection = aenumdirection[l1]; -- long i2 = BlockPos.offset(id, enumdirection); -+ // Paper start -+ int newX = baseX + enumdirection.getStepX(); -+ int newY = baseY + enumdirection.getStepY(); -+ int newZ = baseZ + enumdirection.getStepZ(); -+ long i2 = BlockPos.asLong(newX, newY, newZ); - - if (i2 != excludedId) { -- long j2 = SectionPos.blockToSection(i2); -+ long j2 = SectionPos.blockPosAsSectionLong(newX, newY, newZ); -+ // Paper end - DataLayer nibblearray1; - - if (j1 == j2) { - nibblearray1 = nibblearray; - } else { -- nibblearray1 = ((BlockLightSectionStorage) this.storage).getDataLayer(j2, true); -+ nibblearray1 = ((BlockLightSectionStorage) this.storage).updating.getUpdatingOptimized(j2); // Paper - } - - if (nibblearray1 != null) { -- int k2 = this.computeLevelFromNeighbor(i2, id, this.getLevel(nibblearray1, i2)); -+ int k2 = this.computeLevelFromNeighbor(i2, id, this.getNibbleLightInverse(nibblearray1, newX, newY, newZ)); // Paper - - if (l > k2) { - l = k2; -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java -+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightSectionStorage.java -@@ -0,0 +0,0 @@ - package net.minecraft.world.level.lighting; - - import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; --import net.minecraft.core.BlockPos; --import net.minecraft.core.SectionPos; - import net.minecraft.world.level.LightLayer; - import net.minecraft.world.level.chunk.DataLayer; - import net.minecraft.world.level.chunk.LightChunkGetter; -@@ -0,0 +0,0 @@ public class BlockLightSectionStorage extends LayerLightSectionStorage> 38); -+ int baseY = (int) ((blockPos << 52) >> 52); -+ int baseZ = (int) ((blockPos << 26) >> 38); -+ long j = (((long) (baseX >> 4) & 4194303L) << 42) | (((long) (baseY >> 4) & 1048575L)) | (((long) (baseZ >> 4) & 4194303L) << 20); -+ DataLayer nibblearray = this.e_visible.lookup.apply(j); -+ return nibblearray == null ? 0 : nibblearray.get(baseX & 15, baseY & 15, baseZ & 15); -+ // Paper end - } - - public static final class BlockDataLayerStorageMap extends DataLayerStorageMap { -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java -+++ b/src/main/java/net/minecraft/world/level/lighting/DataLayerStorageMap.java -@@ -0,0 +0,0 @@ import net.minecraft.world.level.chunk.DataLayer; - - public abstract class DataLayerStorageMap> { - -- 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 data; // Paper - avoid copying light data - protected final boolean isVisible; // Paper - avoid copying light data -- java.util.function.Function 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 data, boolean isVisible) { - if (isVisible) { -@@ -0,0 +0,0 @@ public abstract class DataLayerStorageMap> { - } - 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; - } -@@ -0,0 +0,0 @@ public abstract class DataLayerStorageMap> { - 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) MCUtil.scheduleTask(2, updating.cleaner, "Light Engine Release"); // Paper - delay clean incase anything holding ref was still using it - this.clearCache(); - } -@@ -0,0 +0,0 @@ public abstract class DataLayerStorageMap> { - return lookup.apply(chunkPos) != null; // Paper - avoid copying light data - } - -- @Nullable -- public final DataLayer getLayer(long chunkPos) { // Paper - final -- if (this.cacheEnabled) { -- for (int j = 0; j < 2; ++j) { -- if (chunkPos == this.lastSectionKeys[j]) { -- return this.lastSections[j]; -- } -- } -- } -- -- DataLayer nibblearray = lookup.apply(chunkPos); // Paper - avoid copying light data -+ // Paper start - less branching as we know we are using cache and updating -+ public final DataLayer getUpdatingOptimized(final long i) { // Paper - final -+ final DataLayer[] cache = this.cache; -+ if (cache[0].lightCacheKey == i) return cache[0]; -+ if (cache[1].lightCacheKey == i) return cache[1]; - -+ final DataLayer nibblearray = this.lookup.apply(i); // Paper - avoid copying light data - if (nibblearray == null) { - return null; - } else { -- if (this.cacheEnabled) { -- for (int k = 1; k > 0; --k) { -- this.lastSectionKeys[k] = this.lastSectionKeys[k - 1]; -- this.lastSections[k] = this.lastSections[k - 1]; -- } -- -- this.lastSectionKeys[0] = chunkPos; -- this.lastSections[0] = nibblearray; -- } -- -+ cache[1] = cache[0]; -+ cache[0] = nibblearray; - return nibblearray; - } - } -+ // 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) { -@@ -0,0 +0,0 @@ public abstract class DataLayerStorageMap> { - - 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java -+++ b/src/main/java/net/minecraft/world/level/lighting/LayerLightEngine.java -@@ -0,0 +0,0 @@ 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; -@@ -0,0 +0,0 @@ public abstract class LayerLightEngine, S exten - protected final LightLayer layer; - protected final S storage; - private boolean runningLightUpdates; -- protected final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); -+ protected final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); protected final BlockPos.MutableBlockPos pos = pos; // Paper - private final long[] lastChunkPos = new long[2]; -- private final BlockGetter[] lastChunk = new BlockGetter[2]; -+ private final ChunkAccess[] h = 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.a(x >> 4, z >> 4); -+ -+ if (iblockaccess == null) { -+ mutableint.setValue(16); -+ return Blocks.BEDROCK.defaultBlockState(); -+ } else { -+ this.pos.setValues(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.a(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; -@@ -0,0 +0,0 @@ public abstract class LayerLightEngine, S exten - } - - @Nullable -- private BlockGetter getChunk(int chunkX, int chunkZ) { -- long k = ChunkPos.asLong(chunkX, chunkZ); -+ private ChunkAccess a(int i, int j) { // Paper -+ long k = ChunkPos.asLong(i, j); - - for (int l = 0; l < 2; ++l) { - if (k == this.lastChunkPos[l]) { -- return this.lastChunk[l]; -+ return this.h[l]; - } - } - -- BlockGetter iblockaccess = this.chunkSource.getChunkForLighting(chunkX, chunkZ); -+ ChunkAccess iblockaccess = (ChunkAccess) this.chunkSource.getChunkForLighting(i, j); // Paper - - for (int i1 = 1; i1 > 0; --i1) { - this.lastChunkPos[i1] = this.lastChunkPos[i1 - 1]; -- this.lastChunk[i1] = this.lastChunk[i1 - 1]; -+ this.h[i1] = this.h[i1 - 1]; - } - - this.lastChunkPos[0] = k; -- this.lastChunk[0] = iblockaccess; -+ this.h[0] = iblockaccess; - return iblockaccess; - } - - private void clearCache() { - Arrays.fill(this.lastChunkPos, ChunkPos.INVALID_CHUNK_POS); -- Arrays.fill(this.lastChunk, (Object) null); -+ Arrays.fill(this.h, (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 j = SectionPos.blockToSectionCoord(BlockPos.getX(pos)); -- int k = SectionPos.blockToSectionCoord(BlockPos.getZ(pos)); -- BlockGetter iblockaccess = this.getChunk(j, k); -- -- if (iblockaccess == null) { -- if (mutableint != null) { -- mutableint.setValue(16); -- } -- -- return Blocks.BEDROCK.defaultBlockState(); -- } else { -- this.pos.set(pos); -- BlockState iblockdata = iblockaccess.getBlockState(this.pos); -- boolean flag = iblockdata.canOcclude() && iblockdata.useShapeForLightOcclusion(); -- -- if (mutableint != null) { -- mutableint.setValue(iblockdata.getLightBlock(this.chunkSource.getLevel(), (BlockPos) this.pos)); -- } -- -- return flag ? iblockdata : Blocks.AIR.defaultBlockState(); -- } -- } -- } -+ // Paper start - comment out, see getBlockOptimized -+// protected IBlockData a(long i, @Nullable MutableInt mutableint) { -+// if (i == Long.MAX_VALUE) { -+// if (mutableint != null) { -+// mutableint.setValue(0); -+// } -+// -+// return Blocks.AIR.getBlockData(); -+// } else { -+// int j = SectionPosition.a(BlockPosition.b(i)); -+// int k = SectionPosition.a(BlockPosition.d(i)); -+// IBlockAccess iblockaccess = this.a(j, k); -+// -+// if (iblockaccess == null) { -+// if (mutableint != null) { -+// mutableint.setValue(16); -+// } -+// -+// return Blocks.BEDROCK.getBlockData(); -+// } else { -+// this.d.g(i); -+// IBlockData iblockdata = iblockaccess.getType(this.d); -+// boolean flag = iblockdata.l() && iblockdata.e(); -+// -+// if (mutableint != null) { -+// mutableint.setValue(iblockdata.b(this.a.getWorld(), (BlockPosition) this.d)); -+// } -+// -+// return flag ? iblockdata : Blocks.AIR.getBlockData(); -+// } -+// } -+// } -+ // 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(); -@@ -0,0 +0,0 @@ public abstract class LayerLightEngine, S exten - return id == Long.MAX_VALUE ? 0 : 15 - this.storage.getStoredLevel(id); - } - -+ protected int getNibbleLightInverse(DataLayer nibblearray, int x, int y, int z) { return 15 - nibblearray.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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java -+++ b/src/main/java/net/minecraft/world/level/lighting/LayerLightSectionStorage.java -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - 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; // Paper - diff on change, should be "updating" -+ 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 queuedSections = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap()); - private final LongSet untrustedSections = new LongOpenHashSet(); - private final LongSet columnsToRetainQueuedDataFor = new LongOpenHashSet(); -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - 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 data - this.e_visible.disableCache(); // Paper - avoid copying light data - } - -- 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); - } - -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - protected abstract int getLightValue(long blockPos); - - protected int getStoredLevel(long blockPos) { -- long j = SectionPos.blockToSection(blockPos); -- DataLayer nibblearray = this.getDataLayer(j, true); -+ // 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(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos))); -+ return nibblearray.get(x & 15, y & 15, z & 15); // Paper - inline operations -+ // Paper end - } - - protected void setStoredLevel(long blockPos, int value) { -- long k = 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 k = SectionPos.blockPosAsSectionLong(x, y, z); -+ // Paper end - - if (this.changedSections.add(k)) { - this.updatingSectionData.copyDataLayer(k); - } - - DataLayer nibblearray = this.getDataLayer(k, true); -- -- nibblearray.set(SectionPos.sectionRelative(BlockPos.getX(blockPos)), SectionPos.sectionRelative(BlockPos.getY(blockPos)), SectionPos.sectionRelative(BlockPos.getZ(blockPos)), value); -- -- for (int l = -1; l <= 1; ++l) { -- for (int i1 = -1; i1 <= 1; ++i1) { -- for (int j1 = -1; j1 <= 1; ++j1) { -- this.sectionsAffectedByLightUpdates.add(SectionPos.blockToSection(BlockPos.offset(blockPos, i1, j1, l))); -+ nibblearray.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 - } - } - } -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - } - - if (k >= 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.p.remove(i); // Paper -+ //} else { // Paper - this.updatingSectionData.setLayer(id, this.createDataLayer(id)); - this.changedSections.add(id); - this.onNodeAdded(id); - -- for (int l = -1; l <= 1; ++l) { -- for (int i1 = -1; i1 <= 1; ++i1) { -- for (int j1 = -1; j1 <= 1; ++j1) { -- this.sectionsAffectedByLightUpdates.add(SectionPos.blockToSection(BlockPos.offset(id, i1, j1, l))); -+ // 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 - } - } - } -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - return SectionPos.blockToSection(j) == sectionPos; - }); - } else { -- int j = SectionPos.sectionToBlockCoord(SectionPos.x(sectionPos)); -- int k = SectionPos.sectionToBlockCoord(SectionPos.y(sectionPos)); -- int l = SectionPos.sectionToBlockCoord(SectionPos.z(sectionPos)); -+ int j = (int) (sectionPos >> 42) << 4; // Paper - inline -+ int k = (int) (sectionPos << 44 >> 44) << 4; // Paper - inline -+ int l = (int) (sectionPos << 22 >> 42) << 4; // Paper - inline - - for (int i1 = 0; i1 < 16; ++i1) { - for (int j1 = 0; j1 < 16; ++j1) { -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - DataLayer nibblearray; - - while (longiterator.hasNext()) { -- i = (Long) longiterator.next(); -+ i = longiterator.nextLong(); // Paper - this.clearQueuedSectionBlocks(lightProvider, i); - DataLayer nibblearray1 = (DataLayer) this.queuedSections.remove(i); - -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - longiterator = this.toRemove.iterator(); - - while (longiterator.hasNext()) { -- i = (Long) longiterator.next(); -+ i = longiterator.nextLong(); // Paper - this.onNodeRemoved(i); - } - -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - Entry entry; - long j; - -+ DataLayer test = null; // Paper - while (objectiterator.hasNext()) { - entry = (Entry) objectiterator.next(); - j = entry.getLongKey(); -- if (this.storingLightForSection(j)) { -+ if ((test = this.updating.getUpdatingOptimized(j)) != null) { // Paper - dont look up nibble twice - nibblearray = (DataLayer) entry.getValue(); -- if (this.updatingSectionData.getLayer(j) != nibblearray) { -+ if (test != nibblearray) { // Paper - this.clearQueuedSectionBlocks(lightProvider, j); - this.updatingSectionData.setLayer(j, nibblearray); - this.changedSections.add(j); -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - longiterator = this.queuedSections.keySet().iterator(); - - while (longiterator.hasNext()) { -- i = (Long) longiterator.next(); -+ i = longiterator.nextLong(); // Paper - this.checkEdgesForSection(lightProvider, i); - } - } else { - longiterator = this.untrustedSections.iterator(); - - while (longiterator.hasNext()) { -- i = (Long) longiterator.next(); -+ i = longiterator.nextLong(); // Paper - this.checkEdgesForSection(lightProvider, i); - } - } -@@ -0,0 +0,0 @@ public abstract class LayerLightSectionStorage> - - private void checkEdgesForSection(LayerLightEngine lightProvider, long sectionPos) { - if (this.storingLightForSection(sectionPos)) { -- int j = SectionPos.sectionToBlockCoord(SectionPos.x(sectionPos)); -- int k = SectionPos.sectionToBlockCoord(SectionPos.y(sectionPos)); -- int l = 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 j = secX << 4; // baseX -+ int k = secY << 4; // baseY -+ int l = secZ << 4; // baseZ -+ // Paper end - Direction[] aenumdirection = LayerLightSectionStorage.DIRECTIONS; - int i1 = aenumdirection.length; - - for (int j1 = 0; j1 < i1; ++j1) { - Direction enumdirection = aenumdirection[j1]; -- long k1 = SectionPos.offset(sectionPos, enumdirection); -+ long k1 = SectionPos.getAdjacentFromSectionPos(secX, secY, secZ, enumdirection); // Paper - avoid extra unpacking - - if (!this.queuedSections.containsKey(k1) && this.storingLightForSection(k1)) { - for (int l1 = 0; l1 < 16; ++l1) { -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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java -+++ b/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java -@@ -0,0 +0,0 @@ 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; -@@ -0,0 +0,0 @@ public final class SkyLightEngine extends LayerLightEngine= 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 iblockdata = this.getBlockOptimized(jx, jy, jz, mutableint); -+ int blockedLight = mutableint.getValue(); -+ if (blockedLight >= 15) { -+ // Paper end - return 15; - } else { -- int l = BlockPos.getX(sourceId); -- int i1 = BlockPos.getY(sourceId); -- int j1 = BlockPos.getZ(sourceId); -- int k1 = BlockPos.getX(targetId); -- int l1 = BlockPos.getY(targetId); -- int i2 = BlockPos.getZ(targetId); -- boolean flag = l == k1 && j1 == i2; -- int j2 = Integer.signum(k1 - l); -- int k2 = Integer.signum(l1 - i1); -- int l2 = Integer.signum(i2 - j1); -+ // Paper start - inline math -+ int ix = (int) (sourceId >> 38); -+ int iy = (int) ((sourceId << 52) >> 52); -+ int iz = (int) ((sourceId << 26) >> 38); -+ boolean flag = ix == jx && iz == jz; -+ int j2 = Integer.signum(jx - ix); -+ int k2 = Integer.signum(jy - iy); -+ int l2 = Integer.signum(jz - iz); -+ // Paper end - Direction enumdirection; - - if (sourceId == Long.MAX_VALUE) { -@@ -0,0 +0,0 @@ public final class SkyLightEngine extends LayerLightEngine l1; -+ boolean flag1 = sourceId == Long.MAX_VALUE || flag && iy > jy; // Paper rename vars to iy > jy - -- return flag1 && level == 0 && mutableint.getValue() == 0 ? 0 : level + Math.max(1, mutableint.getValue()); -+ return flag1 && level == 0 && blockedLight == 0 ? 0 : level + Math.max(1, blockedLight); // Paper - } - } - } -@@ -0,0 +0,0 @@ public final class SkyLightEngine extends LayerLightEngine> 38); -+ int baseY = (int) ((id << 52) >> 52); -+ int baseZ = (int) ((id << 26) >> 38); -+ long k = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ); -+ int i1 = baseY & 15; -+ int j1 = baseY >> 4; -+ // Paper end - int k1; - - if (i1 != 0) { -@@ -0,0 +0,0 @@ public final class SkyLightEngine extends LayerLightEngine> 38); -+ int baseY = (int) ((id << 52) >> 52); -+ int baseZ = (int) ((id << 26) >> 38); -+ long j1 = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ); -+ DataLayer nibblearray = this.storage.updating.getUpdatingOptimized(j1); -+ // Paper end - Direction[] aenumdirection = SkyLightEngine.DIRECTIONS; - int k1 = aenumdirection.length; - - for (int l1 = 0; l1 < k1; ++l1) { - Direction enumdirection = aenumdirection[l1]; -- long i2 = BlockPos.offset(id, enumdirection); -- long j2 = SectionPos.blockToSection(i2); -+ // Paper start -+ int newX = baseX + enumdirection.getStepX(); -+ int newY = baseY + enumdirection.getStepY(); -+ int newZ = baseZ + enumdirection.getStepZ(); -+ long i2 = BlockPos.asLong(newX, newY, newZ); -+ long j2 = SectionPos.blockPosAsSectionLong(newX, newY, newZ); -+ // Paper end - DataLayer nibblearray1; - - if (j1 == j2) { - nibblearray1 = nibblearray; - } else { -- nibblearray1 = ((SkyLightSectionStorage) this.storage).getDataLayer(j2, true); -+ nibblearray1 = ((SkyLightSectionStorage) this.storage).updating.getUpdatingOptimized(j2); // Paper - } - - if (nibblearray1 != null) { - if (i2 != excludedId) { -- int k2 = this.computeLevelFromNeighbor(i2, id, this.getLevel(nibblearray1, i2)); -+ int k2 = this.computeLevelFromNeighbor(i2, id, this.getNibbleLightInverse(nibblearray1, newX, newY, newZ)); // Paper - - if (l > k2) { - l = k2; -@@ -0,0 +0,0 @@ public final class SkyLightEngine extends LayerLightEngine> 38); -+ int baseY = (int) ((blockPos << 52) >> 52); -+ int baseZ = (int) ((blockPos << 26) >> 38); -+ long j = SectionPos.blockPosAsSectionLong(baseX, baseY, baseZ); -+ // Paper end - int k = SectionPos.y(j); - synchronized (this.visibleUpdateLock) { // Paper - avoid copying light data - SkyLightSectionStorage.SkyDataLayerStorageMap lightenginestoragesky_a = (SkyLightSectionStorage.SkyDataLayerStorageMap) this.e_visible; // Paper - avoid copying light data - must be after lock acquire -@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage> 52) & 15, (int) baseZ & 15); // Paper - y changed above - } else { - return 15; - } -@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage> 42) << 4; // Paper -+ int baseY = (int) (i << 44 >> 44) << 4; // Paper -+ int baseZ = (int) (i << 22 >> 42) << 4; // Paper - j = this.getLevel(i); - if (j != 2 && !this.sectionsToRemoveSourcesFrom.contains(i) && this.sectionsWithSources.add(i)) { - int l; -@@ -0,0 +0,0 @@ public class SkyLightSectionStorage extends LayerLightSectionStorage> 42) << 4; // Paper -+ int baseY = (int) (i << 44 >> 44) << 4; // Paper -+ int baseZ = (int) (i << 22 >> 42) << 4; // Paper - if (this.sectionsWithSources.remove(i) && this.storingLightForSection(i)) { - for (j = 0; j < 16; ++j) { - for (k = 0; k < 16; ++k) { -- long l3 = BlockPos.asLong(SectionPos.sectionToBlockCoord(SectionPos.x(i)) + j, SectionPos.sectionToBlockCoord(SectionPos.y(i)) + 16 - 1, SectionPos.sectionToBlockCoord(SectionPos.z(i)) + k); -+ long l3 = BlockPos.asLong(baseX + j, baseY + 16 - 1, baseZ + k); // Paper - - lightProvider.checkEdge(Long.MAX_VALUE, l3, 15, false); - } diff --git a/patches/server-remapped/Optimize-WorldBorder-collision-checks-and-air.patch b/patches/server-remapped/Optimize-WorldBorder-collision-checks-and-air.patch deleted file mode 100644 index 1bbbb55a8d..0000000000 --- a/patches/server-remapped/Optimize-WorldBorder-collision-checks-and-air.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Sun, 10 May 2020 22:49:05 -0400 -Subject: [PATCH] Optimize WorldBorder collision checks and air - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -0,0 +0,0 @@ import net.minecraft.world.phys.AABB; - import net.minecraft.world.phys.HitResult; - import net.minecraft.world.phys.Vec2; - import net.minecraft.world.phys.Vec3; --import net.minecraft.world.phys.shapes.BooleanOp; - import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.Shapes; - import net.minecraft.world.phys.shapes.VoxelShape; -@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s - AABB axisalignedbb = this.getBoundingBox(); - CollisionContext voxelshapecollision = CollisionContext.of(this); - VoxelShape voxelshape = this.level.getWorldBorder().getCollisionShape(); -- Stream stream = Shapes.joinIsNotEmpty(voxelshape, Shapes.create(axisalignedbb.deflate(1.0E-7D)), BooleanOp.AND) ? Stream.empty() : Stream.of(voxelshape); -+ Stream stream = !this.level.getWorldBorder().isInBounds(axisalignedbb) ? Stream.empty() : Stream.of(voxelshape); // Paper - Stream stream1 = this.level.getEntityCollisions(this, axisalignedbb.expandTowards(movement), (entity) -> { - return true; - }); -diff --git a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java -+++ b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java -@@ -0,0 +0,0 @@ public class CollisionSpliterator extends AbstractSpliterator { - AABB axisalignedbb = this.source.getBoundingBox(); - - if (!isBoxFullyWithinWorldBorder(worldborder, axisalignedbb)) { -- VoxelShape voxelshape = worldborder.getCollisionShape(); -- -- if (!isOutsideBorder(voxelshape, axisalignedbb) && isCloseToBorder(voxelshape, axisalignedbb)) { -- consumer.accept(voxelshape); -+ // Paper start -+ if (worldborder.isInBounds(axisalignedbb.deflate(1.0E-7D)) && !worldborder.isInBounds(axisalignedbb.grow(1.0E-7D))) { -+ consumer.accept(worldborder.asVoxelShape()); -+ // Paper end - return true; - } - } -diff --git a/src/main/java/net/minecraft/world/level/border/WorldBorder.java b/src/main/java/net/minecraft/world/level/border/WorldBorder.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/level/border/WorldBorder.java -+++ b/src/main/java/net/minecraft/world/level/border/WorldBorder.java -@@ -0,0 +0,0 @@ public class WorldBorder { - return (double) pos.getMaxBlockX() > this.getMinX() && (double) pos.getMinBlockX() < this.getMaxX() && (double) pos.getMaxBlockZ() > this.getMinZ() && (double) pos.getMinBlockZ() < this.getMaxZ(); - } - -+ public final boolean isInBounds(AABB aabb) { return this.isWithinBounds(aabb); } // Paper - OBFHELPER - public boolean isWithinBounds(AABB box) { - return box.maxX > this.getMinX() && box.minX < this.getMaxX() && box.maxZ > this.getMinZ() && box.minZ < this.getMaxZ(); - } -diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java -+++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java -@@ -0,0 +0,0 @@ public final class Shapes { - BlockState iblockdata = world.getTypeIfLoaded(blockposition_mutableblockposition); // Paper - if (iblockdata == null) return 0.0D; // Paper - -- if ((k2 != 1 || iblockdata.hasLargeCollisionShape()) && (k2 != 2 || iblockdata.is(Blocks.MOVING_PISTON))) { -+ if (!iblockdata.isAir() && (k2 != 1 || iblockdata.hasLargeCollisionShape()) && (k2 != 2 || iblockdata.is(Blocks.MOVING_PISTON))) { // Paper - initial = iblockdata.getCollisionShape((BlockGetter) world, blockposition_mutableblockposition, context).collide(enumdirection_enumaxis2, box.move((double) (-blockposition_mutableblockposition.getX()), (double) (-blockposition_mutableblockposition.getY()), (double) (-blockposition_mutableblockposition.getZ())), initial); - if (Math.abs(initial) < 1.0E-7D) { - return 0.0D; diff --git a/patches/server-remapped/Add-Plugin-Tickets-to-API-Chunk-Methods.patch b/patches/server/Add-Plugin-Tickets-to-API-Chunk-Methods.patch similarity index 71% rename from patches/server-remapped/Add-Plugin-Tickets-to-API-Chunk-Methods.patch rename to patches/server/Add-Plugin-Tickets-to-API-Chunk-Methods.patch index f7e2ed1c82..ad599f25f1 100644 --- a/patches/server-remapped/Add-Plugin-Tickets-to-API-Chunk-Methods.patch +++ b/patches/server/Add-Plugin-Tickets-to-API-Chunk-Methods.patch @@ -26,35 +26,27 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -0,0 +0,0 @@ public final class CraftServer implements Server { - ambientSpawn = configuration.getInt("spawn-limits.ambient"); - console.autosavePeriod = configuration.getInt("ticks-per.autosave"); - warningState = WarningState.value(configuration.getString("settings.deprecated-verbose")); -- TicketType.PLUGIN.timeout = configuration.getInt("chunk-gc.period-in-ticks"); -+ TicketType.PLUGIN.timeout = Math.min(20, configuration.getInt("chunk-gc.period-in-ticks")); // Paper - cap plugin loads to 1 second - minimumAPI = configuration.getString("settings.minimum-api"); - loadIcon(); + this.ambientSpawn = this.configuration.getInt("spawn-limits.ambient"); + console.autosavePeriod = this.configuration.getInt("ticks-per.autosave"); + this.warningState = WarningState.value(this.configuration.getString("settings.deprecated-verbose")); +- TicketType.PLUGIN.timeout = this.configuration.getInt("chunk-gc.period-in-ticks"); ++ TicketType.PLUGIN.timeout = Math.min(20, this.configuration.getInt("chunk-gc.period-in-ticks")); // Paper - cap plugin loads to 1 second + this.minimumAPI = this.configuration.getString("settings.minimum-api"); + this.loadIcon(); } @@ -0,0 +0,0 @@ public final class CraftServer implements Server { - waterAmbientSpawn = configuration.getInt("spawn-limits.water-ambient"); - ambientSpawn = configuration.getInt("spawn-limits.ambient"); - warningState = WarningState.value(configuration.getString("settings.deprecated-verbose")); -- TicketType.PLUGIN.timeout = configuration.getInt("chunk-gc.period-in-ticks"); + this.waterAmbientSpawn = this.configuration.getInt("spawn-limits.water-ambient"); + this.ambientSpawn = this.configuration.getInt("spawn-limits.ambient"); + this.warningState = WarningState.value(this.configuration.getString("settings.deprecated-verbose")); +- TicketType.PLUGIN.timeout = this.configuration.getInt("chunk-gc.period-in-ticks"); + TicketType.PLUGIN.timeout = Math.min(20, configuration.getInt("chunk-gc.period-in-ticks")); // Paper - cap plugin loads to 1 second - minimumAPI = configuration.getString("settings.minimum-api"); - printSaveWarning = false; - console.autosavePeriod = configuration.getInt("ticks-per.autosave"); + this.minimumAPI = this.configuration.getString("settings.minimum-api"); + this.printSaveWarning = false; + console.autosavePeriod = this.configuration.getInt("ticks-per.autosave"); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -0,0 +0,0 @@ import net.minecraft.network.protocol.game.ClientboundCustomSoundPacket; - import net.minecraft.network.protocol.game.ClientboundLevelEventPacket; - import net.minecraft.network.protocol.game.ClientboundSetTimePacket; - import net.minecraft.resources.ResourceLocation; -+import net.minecraft.server.MCUtil; - import net.minecraft.server.level.ChunkHolder; - import net.minecraft.server.level.ChunkMap; - import net.minecraft.server.level.DistanceManager; @@ -0,0 +0,0 @@ public class CraftWorld implements World { @Override @@ -72,7 +64,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + // Paper start + private void addTicket(int x, int z) { -+ MCUtil.MAIN_EXECUTOR.execute(() -> world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE)); // Paper ++ net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE)); // Paper } + // Paper end @@ -81,9 +73,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ public class CraftWorld implements World { public boolean unloadChunkRequest(int x, int z) { org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot - if (isChunkLoaded(x, z)) { -- world.getChunkSource().removeRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE); -+ world.getChunkSource().removeRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE); // Paper + if (this.isChunkLoaded(x, z)) { +- this.world.getChunkSource().removeRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE); ++ this.world.getChunkSource().removeRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE); // Paper } return true; @@ -120,10 +112,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 return true; // Paper end @@ -0,0 +0,0 @@ public class CraftWorld implements World { - } + return this.world.getChunkSource().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null); + if (chunk != null) addTicket(x, z); // Paper - return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); + return java.util.concurrent.CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); }, net.minecraft.server.MinecraftServer.getServer()); } diff --git a/patches/server-remapped/Add-and-implement-PlayerRecipeBookClickEvent.patch b/patches/server/Add-and-implement-PlayerRecipeBookClickEvent.patch similarity index 84% rename from patches/server-remapped/Add-and-implement-PlayerRecipeBookClickEvent.patch rename to patches/server/Add-and-implement-PlayerRecipeBookClickEvent.patch index 381343eb55..351e9bb0c8 100644 --- a/patches/server-remapped/Add-and-implement-PlayerRecipeBookClickEvent.patch +++ b/patches/server/Add-and-implement-PlayerRecipeBookClickEvent.patch @@ -8,22 +8,20 @@ diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListener index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener { +@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); this.player.resetLastActionTime(); - if (!this.player.isSpectator() && this.player.containerMenu.containerId == packet.getContainerId() && this.player.containerMenu.isSynched(this.player) && this.player.containerMenu instanceof RecipeBookMenu) { + if (!this.player.isSpectator() && this.player.containerMenu.containerId == packet.getContainerId() && this.player.containerMenu instanceof RecipeBookMenu) { - this.server.getRecipeManager().byKey(packet.getRecipe()).ifPresent((irecipe) -> { - ((RecipeBookMenu) this.player.containerMenu).handlePlacement(packet.isShiftDown(), irecipe, this.player); -- }); + // Paper start - fire event for clicking recipes in the recipe book + com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent event = new com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent( + player.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(packet.getRecipe()), packet.isShiftDown()); + if (event.callEvent()) { + this.server.getRecipeManager().byKey(org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(event.getRecipe())).ifPresent((irecipe) -> { -+ ((ContainerRecipeBook) this.player.activeContainer).a(event.isMakeAll(), irecipe, this.player); -+ }); -+ } -+ // Paper end ++ ((RecipeBookMenu) this.player.containerMenu).handlePlacement(event.isMakeAll(), irecipe, this.player); + }); ++ } // Paper end } } diff --git a/patches/server-remapped/Add-permission-for-command-blocks.patch b/patches/server/Add-permission-for-command-blocks.patch similarity index 80% rename from patches/server-remapped/Add-permission-for-command-blocks.patch rename to patches/server/Add-permission-for-command-blocks.patch index 55aefd8fc0..be6324bc19 100644 --- a/patches/server-remapped/Add-permission-for-command-blocks.patch +++ b/patches/server/Add-permission-for-command-blocks.patch @@ -12,8 +12,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 BlockEntity tileentity = this.level.getBlockEntity(pos); Block block = iblockdata.getBlock(); -- if ((block instanceof CommandBlock || block instanceof StructureBlock || block instanceof JigsawBlock) && !this.player.canUseGameMasterBlocks()) { -+ if ((block instanceof CommandBlock || block instanceof StructureBlock || block instanceof JigsawBlock) && !this.player.canUseGameMasterBlocks() && !(block instanceof CommandBlock && (this.player.isCreative() && this.player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission +- if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks()) { ++ if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks() && !(block instanceof net.minecraft.world.level.block.CommandBlock && (this.player.isCreative() && this.player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission this.level.sendBlockUpdated(pos, iblockdata, iblockdata, 3); return false; } else if (this.player.blockActionRestricted((Level) this.level, pos, this.gameModeForPlayer)) { @@ -21,7 +21,7 @@ diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListener index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener { +@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); if (!this.server.isCommandBlockEnabled()) { this.player.sendMessage(new TranslatableComponent("advMode.notEnabled"), Util.NIL_UUID); @@ -30,7 +30,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 this.player.sendMessage(new TranslatableComponent("advMode.notAllowed"), Util.NIL_UUID); } else { BaseCommandBlock commandblocklistenerabstract = null; -@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener { +@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); if (!this.server.isCommandBlockEnabled()) { this.player.sendMessage(new TranslatableComponent("advMode.notEnabled"), Util.NIL_UUID); @@ -56,7 +56,7 @@ diff --git a/src/main/java/net/minecraft/world/level/block/CommandBlock.java b/s index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/block/CommandBlock.java +++ b/src/main/java/net/minecraft/world/level/block/CommandBlock.java -@@ -0,0 +0,0 @@ public class CommandBlock extends BaseEntityBlock { +@@ -0,0 +0,0 @@ public class CommandBlock extends BaseEntityBlock implements GameMasterBlock { public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { BlockEntity tileentity = world.getBlockEntity(pos); @@ -70,10 +70,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java +++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java @@ -0,0 +0,0 @@ public final class CraftDefaultPermissions { - DefaultPermissions.registerPermission(ROOT + ".nbt.copy", "Gives the user the ability to copy NBT in creative", org.bukkit.permissions.PermissionDefault.TRUE, parent); - DefaultPermissions.registerPermission(ROOT + ".debugstick", "Gives the user the ability to use the debug stick in creative", org.bukkit.permissions.PermissionDefault.OP, parent); - DefaultPermissions.registerPermission(ROOT + ".debugstick.always", "Gives the user the ability to use the debug stick in all game modes", org.bukkit.permissions.PermissionDefault.FALSE, parent); -+ DefaultPermissions.registerPermission(ROOT + ".commandblock", "Gives the user the ability to use command blocks.", org.bukkit.permissions.PermissionDefault.OP, parent); // Paper + DefaultPermissions.registerPermission(CraftDefaultPermissions.ROOT + ".nbt.copy", "Gives the user the ability to copy NBT in creative", org.bukkit.permissions.PermissionDefault.TRUE, parent); + DefaultPermissions.registerPermission(CraftDefaultPermissions.ROOT + ".debugstick", "Gives the user the ability to use the debug stick in creative", org.bukkit.permissions.PermissionDefault.OP, parent); + DefaultPermissions.registerPermission(CraftDefaultPermissions.ROOT + ".debugstick.always", "Gives the user the ability to use the debug stick in all game modes", org.bukkit.permissions.PermissionDefault.FALSE, parent); ++ DefaultPermissions.registerPermission(CraftDefaultPermissions.ROOT + ".commandblock", "Gives the user the ability to use command blocks.", org.bukkit.permissions.PermissionDefault.OP, parent); // Paper // Spigot end parent.recalculatePermissibles(); } diff --git a/patches/server-remapped/Clean-up-duplicated-GameProfile-Properties.patch b/patches/server/Clean-up-duplicated-GameProfile-Properties.patch similarity index 51% rename from patches/server-remapped/Clean-up-duplicated-GameProfile-Properties.patch rename to patches/server/Clean-up-duplicated-GameProfile-Properties.patch index 60008dbd21..11bc0d037f 100644 --- a/patches/server-remapped/Clean-up-duplicated-GameProfile-Properties.patch +++ b/patches/server/Clean-up-duplicated-GameProfile-Properties.patch @@ -13,46 +13,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/nbt/NbtUtils.java +++ b/src/main/java/net/minecraft/nbt/NbtUtils.java @@ -0,0 +0,0 @@ public final class NbtUtils { - while (iterator.hasNext()) { - String s1 = (String) iterator.next(); - ListTag nbttaglist = nbttagcompound1.getList(s1, 10); -- -- for (int i = 0; i < nbttaglist.size(); ++i) { -+ if (nbttaglist.size() == 0) continue; // Paper - remove duplicate properties -+ for (int i = nbttaglist.size() - 1; i < nbttaglist.size(); ++i) { // Paper - remove duplicate properties - CompoundTag nbttagcompound2 = nbttaglist.getCompound(i); - String s2 = nbttagcompound2.getString("Value"); + for(String string2 : compoundTag.getAllKeys()) { + ListTag listTag = compoundTag.getList(string2, 10); -@@ -0,0 +0,0 @@ public final class NbtUtils { - Optional optional = property.getValue(propertiesTag.getString(key)); - - if (optional.isPresent()) { -- return (StateHolder) state.setValue(property, (Comparable) optional.get()); -+ return state.setValue(property, optional.get()); // Paper - decompile error - } else { - NbtUtils.LOGGER.warn("Unable to read property: {} with value: {} for blockstate: {}", key, propertiesTag.getString(key), mainTag.toString()); - return state; -@@ -0,0 +0,0 @@ public final class NbtUtils { - return nbttagcompound; - } - -- private static > String getName(net.minecraft.world.level.block.state.properties.Property property, Comparable value) { -- return property.value(value); -+ private static > String getName(net.minecraft.world.level.block.state.properties.Property property, Comparable value) {// Paper - decompile error -+ return property.getName((T) value);// Paper - decompile error - } - - public static CompoundTag update(DataFixer fixer, DataFixTypes fixTypes, CompoundTag tag, int oldVersion) { +- for(int i = 0; i < listTag.size(); ++i) { ++ if (listTag.size() == 0) continue; // Paper - remove duplicate properties ++ for (int i = listTag.size() - 1; i < listTag.size(); ++i) { // Paper - remove duplicate properties + CompoundTag compoundTag2 = listTag.getCompound(i); + String string3 = compoundTag2.getString("Value"); + if (compoundTag2.contains("Signature", 8)) { diff --git a/src/main/java/net/minecraft/world/item/PlayerHeadItem.java b/src/main/java/net/minecraft/world/item/PlayerHeadItem.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/item/PlayerHeadItem.java +++ b/src/main/java/net/minecraft/world/item/PlayerHeadItem.java @@ -0,0 +0,0 @@ public class PlayerHeadItem extends StandingAndWallBlockItem { - return true; - } else { + }); // CraftBukkit start + } else { + // Paper start - clean up old duplicated properties -+ CompoundTag properties = tag.getCompound("SkullOwner").getCompound("Properties"); ++ CompoundTag properties = nbt.getCompound("SkullOwner").getCompound("Properties"); + for (String key : properties.getAllKeys()) { + net.minecraft.nbt.ListTag values = properties.getList(key, 10); + if (values.size() > 1) { @@ -63,6 +42,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + } + // Paper end - net.minecraft.nbt.ListTag textures = tag.getCompound("SkullOwner").getCompound("Properties").getList("textures", 10); // Safe due to method contracts + net.minecraft.nbt.ListTag textures = nbt.getCompound("SkullOwner").getCompound("Properties").getList("textures", 10); // Safe due to method contracts for (int i = 0; i < textures.size(); i++) { if (textures.get(i) instanceof CompoundTag && !((CompoundTag) textures.get(i)).contains("Signature", 8) && ((CompoundTag) textures.get(i)).getString("Value").trim().isEmpty()) { diff --git a/patches/server-remapped/Delay-Chunk-Unloads-based-on-Player-Movement.patch b/patches/server/Delay-Chunk-Unloads-based-on-Player-Movement.patch similarity index 84% rename from patches/server-remapped/Delay-Chunk-Unloads-based-on-Player-Movement.patch rename to patches/server/Delay-Chunk-Unloads-based-on-Player-Movement.patch index ccd1a2f36a..444f391c08 100644 --- a/patches/server-remapped/Delay-Chunk-Unloads-based-on-Player-Movement.patch +++ b/patches/server/Delay-Chunk-Unloads-based-on-Player-Movement.patch @@ -21,10 +21,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { - private void viewDistance() { this.noTickViewDistance = this.getInt("viewdistances.no-tick-view-distance", -1); } -+ + + public long delayChunkUnloadsBy; + private void delayChunkUnloadsBy() { + delayChunkUnloadsBy = PaperConfig.getSeconds(getString("delay-chunk-unloads-by", "10s")); @@ -33,7 +32,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + delayChunkUnloadsBy *= 20; + } + } - } ++ + public boolean altItemDespawnRateEnabled; + public Map altItemDespawnRateMap; + private void altItemDespawnRate() { diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/DistanceManager.java @@ -56,7 +58,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + if (!hasPlayer && playerChunk != null && playerChunk.isFullChunkReady()) { + Ticket delayUnload = new Ticket(TicketType.DELAY_UNLOAD, 33, i); + delayUnload.delayUnloadBy = delayChunkUnloadsBy; -+ delayUnload.setCurrentTick(this.ticketTickCounter); ++ delayUnload.setCreatedTick(this.ticketTickCounter); + arraysetsorted.remove(delayUnload); + // refresh ticket + arraysetsorted.add(delayUnload); @@ -71,9 +73,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/server/level/Ticket.java +++ b/src/main/java/net/minecraft/server/level/Ticket.java @@ -0,0 +0,0 @@ public final class Ticket implements Comparable> { + private final int ticketLevel; public final T key; public final T getObjectReason() { return this.key; } // Paper - OBFHELPER private long createdTick; public final long getCreationTick() { return this.createdTick; } // Paper - OBFHELPER - public int priority = 0; // Paper + public long delayUnloadBy; // Paper protected Ticket(TicketType type, int level, T argument) { @@ -83,25 +85,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + this.delayUnloadBy = type.timeout; // Paper } - public int compareTo(Ticket ticket) { + @Override @@ -0,0 +0,0 @@ public final class Ticket implements Comparable> { } protected boolean timedOut(long currentTick) { -- long j = this.type.timeout(); -+ long j = delayUnloadBy; // Paper - - return j != 0L && currentTick - this.createdTick > j; +- long l = this.type.timeout(); ++ long l = delayUnloadBy; // Paper + return l != 0L && currentTick - this.createdTick > l; } + } diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/level/TicketType.java +++ b/src/main/java/net/minecraft/server/level/TicketType.java @@ -0,0 +0,0 @@ public class TicketType { - public static final TicketType ASYNC_LOAD = create("async_load", Long::compareTo); // Paper - public static final TicketType PRIORITY = create("priority", Comparator.comparingLong(ChunkPos::toLong), 300); // Paper - public static final TicketType URGENT = create("urgent", Comparator.comparingLong(ChunkPos::toLong), 300); // Paper + public static final TicketType UNKNOWN = TicketType.create("unknown", Comparator.comparingLong(ChunkPos::toLong), 1); + public static final TicketType PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit + public static final TicketType PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit + public static final TicketType DELAY_UNLOAD = create("delay_unload", Long::compareTo, 300); // Paper - public static TicketType create(String name, Comparator comparator) { - return new TicketType<>(name, comparator, 0L); + public static TicketType create(String name, Comparator argumentComparator) { + return new TicketType<>(name, argumentComparator, 0L); diff --git a/patches/server-remapped/Don-t-allow-null-UUID-s-for-chat.patch b/patches/server/Don-t-allow-null-UUID-s-for-chat.patch similarity index 65% rename from patches/server-remapped/Don-t-allow-null-UUID-s-for-chat.patch rename to patches/server/Don-t-allow-null-UUID-s-for-chat.patch index e72a990071..227b8a9644 100644 --- a/patches/server-remapped/Don-t-allow-null-UUID-s-for-chat.patch +++ b/patches/server/Don-t-allow-null-UUID-s-for-chat.patch @@ -8,20 +8,12 @@ diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundChatPa index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/network/protocol/game/ClientboundChatPacket.java +++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundChatPacket.java -@@ -0,0 +0,0 @@ package net.minecraft.network.protocol.game; - - import java.io.IOException; - import java.util.UUID; -+import net.minecraft.Util; - import net.minecraft.network.FriendlyByteBuf; - import net.minecraft.network.chat.ChatType; - import net.minecraft.network.chat.Component; @@ -0,0 +0,0 @@ public class ClientboundChatPacket implements Packet { - public ClientboundChatPacket(Component message, ChatType location, UUID senderUuid) { + public ClientboundChatPacket(Component message, ChatType location, UUID sender) { this.message = message; this.type = location; -- this.sender = senderUuid; -+ this.sender = senderUuid != null ? senderUuid : Util.getNullUUID(); // Paper +- this.sender = sender; ++ this.sender = sender != null ? sender : net.minecraft.Util.NIL_UUID; } - @Override + public ClientboundChatPacket(FriendlyByteBuf buf) { diff --git a/patches/server/Ensure-Entity-AABB-s-are-never-invalid.patch b/patches/server/Ensure-Entity-AABB-s-are-never-invalid.patch new file mode 100644 index 0000000000..97eb5627be --- /dev/null +++ b/patches/server/Ensure-Entity-AABB-s-are-never-invalid.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 10 May 2020 22:12:46 -0400 +Subject: [PATCH] Ensure Entity AABB's are never invalid + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + + public void setPos(double x, double y, double z) { + this.setPosRaw(x, y, z); +- this.setBoundingBox(this.makeBoundingBox()); ++ // this.setBoundingBox(this.makeBoundingBox()); // Paper - move into setPositionRaw + } + + protected AABB makeBoundingBox() { +@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + } + + public final void setPosRaw(double x, double y, double z) { ++ // Paper start - never allow AABB to become desynced from position ++ // hanging has its own special logic ++ if (!(this instanceof net.minecraft.world.entity.decoration.HangingEntity) && (this.position.x != x || this.position.y != y || this.position.z != z)) { ++ this.setBoundingBox(this.dimensions.makeBoundingBox(x, y, z)); ++ } ++ // Paper end + if (this.position.x != x || this.position.y != y || this.position.z != z) { + this.position = new Vec3(x, y, z); + int i = Mth.floor(x); diff --git a/patches/server-remapped/Expose-Arrow-getItemStack.patch b/patches/server/Expose-Arrow-getItemStack.patch similarity index 51% rename from patches/server-remapped/Expose-Arrow-getItemStack.patch rename to patches/server/Expose-Arrow-getItemStack.patch index 3be478859e..0c7af0da2c 100644 --- a/patches/server-remapped/Expose-Arrow-getItemStack.patch +++ b/patches/server/Expose-Arrow-getItemStack.patch @@ -4,30 +4,18 @@ Date: Sat, 23 May 2020 10:31:11 -0400 Subject: [PATCH] Expose Arrow getItemStack -diff --git a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java -@@ -0,0 +0,0 @@ public abstract class AbstractArrow extends Projectile { - } - } - -+ public final ItemStack getOriginalItemStack() { return getPickupItem(); } // Paper - OBFHELPER - exists purely due to overrides all as protected and dont want to change them all - protected abstract ItemStack getPickupItem(); - - @Override diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java @@ -0,0 +0,0 @@ public class CraftArrow extends AbstractProjectile implements AbstractArrow { - getHandle().pickup = net.minecraft.world.entity.projectile.AbstractArrow.Pickup.byOrdinal(status.ordinal()); + this.getHandle().pickup = net.minecraft.world.entity.projectile.AbstractArrow.Pickup.byOrdinal(status.ordinal()); } + // Paper start + @Override + public org.bukkit.craftbukkit.inventory.CraftItemStack getItemStack() { -+ return org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(getHandle().getOriginalItemStack()); ++ return org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(getHandle().getPickupItem()); + } + //Paper end + diff --git a/patches/server-remapped/Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch b/patches/server/Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch similarity index 84% rename from patches/server-remapped/Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch rename to patches/server/Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch index 5c269af548..5bbcd4069f 100644 --- a/patches/server-remapped/Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch +++ b/patches/server/Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch @@ -14,8 +14,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @Override public void runTaskTimerAsynchronously(Plugin plugin, Consumer task, long delay, long period) throws IllegalArgumentException { -- runTaskTimerAsynchronously(plugin, (Object) task, delay, CraftTask.NO_REPEATING); -+ runTaskTimerAsynchronously(plugin, (Object) task, delay, period); +- this.runTaskTimerAsynchronously(plugin, (Object) task, delay, CraftTask.NO_REPEATING); ++ this.runTaskTimerAsynchronously(plugin, (Object) task, delay, period); } @Override diff --git a/patches/server-remapped/Fix-Per-World-Difficulty-Remembering-Difficulty.patch b/patches/server/Fix-Per-World-Difficulty-Remembering-Difficulty.patch similarity index 68% rename from patches/server-remapped/Fix-Per-World-Difficulty-Remembering-Difficulty.patch rename to patches/server/Fix-Per-World-Difficulty-Remembering-Difficulty.patch index 9912dd17fd..b4a7117522 100644 --- a/patches/server-remapped/Fix-Per-World-Difficulty-Remembering-Difficulty.patch +++ b/patches/server/Fix-Per-World-Difficulty-Remembering-Difficulty.patch @@ -15,19 +15,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } } -- public void setDifficulty(Difficulty enumdifficulty, boolean forceUpdate) { +- public void setDifficulty(Difficulty difficulty, boolean forceUpdate) { - if (forceUpdate || !this.worldData.isDifficultyLocked()) { -- this.worldData.setDifficulty(this.worldData.isHardcore() ? Difficulty.HARD : enumdifficulty); +- this.worldData.setDifficulty(this.worldData.isHardcore() ? Difficulty.HARD : difficulty); - this.updateMobSpawningFlags(); - this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate); -+ // Paper start - fix per world difficulty -+ public void setWorldDifficulty(ServerLevel world, Difficulty enumdifficulty, boolean forcefullySet) { this.a(world, enumdifficulty, forcefullySet); } -+ public void a(ServerLevel world, Difficulty enumdifficulty, boolean flag) { -+ PrimaryLevelData worldData = world.worldDataServer; -+ if (flag || !worldData.isDifficultyLocked()) { -+ worldData.setDifficulty(worldData.isHardcore() ? Difficulty.HARD : enumdifficulty); -+ world.setSpawnSettings(worldData.getDifficulty() != Difficulty.PEACEFUL && ((DedicatedServer) this).settings.getProperties().spawnMonsters, this.isSpawningAnimals()); -+ //this.getPlayerList().getPlayers().forEach(this::b); // Commented: WorldDataServer#setDifficulty handles updating players' difficulties ++ // Paper start - remember per level difficulty ++ public void setDifficulty(ServerLevel level, Difficulty difficulty, boolean forceUpdate) { ++ PrimaryLevelData worldData = level.serverLevelData; ++ if (forceUpdate || !worldData.isDifficultyLocked()) { ++ worldData.setDifficulty(worldData.isHardcore() ? Difficulty.HARD : difficulty); ++ level.setSpawnSettings(worldData.getDifficulty() != Difficulty.PEACEFUL && ((DedicatedServer) this).settings.getProperties().spawnMonsters, this.isSpawningAnimals()); ++ // this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate); + // Paper end } } @@ -36,26 +35,18 @@ diff --git a/src/main/java/net/minecraft/server/commands/DifficultyCommand.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/commands/DifficultyCommand.java +++ b/src/main/java/net/minecraft/server/commands/DifficultyCommand.java -@@ -0,0 +0,0 @@ import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; - import net.minecraft.commands.CommandSourceStack; - import net.minecraft.network.chat.TranslatableComponent; - import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerLevel; - import net.minecraft.world.Difficulty; - - public class DifficultyCommand { @@ -0,0 +0,0 @@ public class DifficultyCommand { - public static int setDifficulty(CommandSourceStack source, Difficulty difficulty) throws CommandSyntaxException { - MinecraftServer minecraftserver = source.getServer(); -- if (minecraftserver.getWorldData().getDifficulty() == difficulty) { -+ ServerLevel world = source.getLevel(); // Paper -+ if (world.worldDataServer.getDifficulty() == difficulty) { // Paper - throw DifficultyCommand.ERROR_ALREADY_DIFFICULT.create(difficulty.getKey()); + public static int setDifficulty(CommandSourceStack source, Difficulty difficulty) throws CommandSyntaxException { + MinecraftServer minecraftServer = source.getServer(); +- if (minecraftServer.getWorldData().getDifficulty() == difficulty) { ++ net.minecraft.server.level.ServerLevel level = source.getLevel(); // Paper ++ if (level.serverLevelData.getDifficulty() == difficulty) { // Paper + throw ERROR_ALREADY_DIFFICULT.create(difficulty.getKey()); } else { -- minecraftserver.setDifficulty(difficulty, true); -+ minecraftserver.a(world, difficulty, true); // Paper - source.sendSuccess(new TranslatableComponent("commands.difficulty.success", new Object[]{difficulty.getDisplayName()}), true); +- minecraftServer.setDifficulty(difficulty, true); ++ minecraftServer.setDifficulty(level, difficulty, true); // Paper - use world + source.sendSuccess(new TranslatableComponent("commands.difficulty.success", difficulty.getDisplayName()), true); return 0; } diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -75,7 +66,7 @@ diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListener index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener { +@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser public void handleChangeDifficulty(ServerboundChangeDifficultyPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel()); if (this.player.hasPermissions(2) || this.isSingleplayerOwner()) { diff --git a/patches/server-remapped/Fix-missing-chunks-due-to-integer-overflow.patch b/patches/server/Fix-missing-chunks-due-to-integer-overflow.patch similarity index 85% rename from patches/server-remapped/Fix-missing-chunks-due-to-integer-overflow.patch rename to patches/server/Fix-missing-chunks-due-to-integer-overflow.patch index 4c44d36680..83c39000f2 100644 --- a/patches/server-remapped/Fix-missing-chunks-due-to-integer-overflow.patch +++ b/patches/server/Fix-missing-chunks-due-to-integer-overflow.patch @@ -18,12 +18,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +++ b/src/main/java/net/minecraft/world/level/biome/TheEndBiomeSource.java @@ -0,0 +0,0 @@ public class TheEndBiomeSource extends BiomeSource { int l = j / 2; - int i1 = i % 2; - int j1 = j % 2; -- float f = 100.0F - Mth.sqrt((float) (i * i + j * j)) * 8.0F; -+ // Paper start - cast ints to long to avoid integer overflow -+ float f = 100.0F - Mth.sqrt((long) i * (long) i + (long) j * (long) j) * 8.0F; -+ // Paper end - + int m = i % 2; + int n = j % 2; +- float f = 100.0F - Mth.sqrt((float)(i * i + j * j)) * 8.0F; ++ float f = 100.0F - Mth.sqrt((long) i * (long) i + (long) j * (long) j) * 8.0F; // Paper - cast ints to long to avoid integer overflow f = Mth.clamp(f, -100.0F, 80.0F); + for(int o = -12; o <= 12; ++o) { diff --git a/patches/server-remapped/Fix-piston-physics-inconsistency-MC-188840.patch b/patches/server/Fix-piston-physics-inconsistency-MC-188840.patch similarity index 77% rename from patches/server-remapped/Fix-piston-physics-inconsistency-MC-188840.patch rename to patches/server/Fix-piston-physics-inconsistency-MC-188840.patch index 3bf0ce8bd1..a9fbc01d90 100644 --- a/patches/server-remapped/Fix-piston-physics-inconsistency-MC-188840.patch +++ b/patches/server/Fix-piston-physics-inconsistency-MC-188840.patch @@ -63,14 +63,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end - fix a variety of piston desync dupes blockposition3 = blockposition3.relative(enumdirection1); map.remove(blockposition3); - world.setBlock(blockposition3, (BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(PistonBaseBlock.FACING, dir), 68); -- world.setBlockEntity(blockposition3, MovingPistonBlock.newMovingBlockEntity((BlockState) list1.get(k), dir, retract, false)); + BlockState iblockdata2 = (BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(PistonBaseBlock.FACING, dir); + + world.setBlock(blockposition3, iblockdata2, 68); +- world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockposition3, iblockdata2, (BlockState) list1.get(k), dir, retract, false)); + // Paper start - fix a variety of piston desync dupes + if (!allowDesync) { + iblockdata1 = world.getBlockState(oldPos); + map.replace(oldPos, iblockdata1); + } -+ world.setBlockEntity(blockposition3, MovingPistonBlock.newMovingBlockEntity(allowDesync ? list1.get(k) : iblockdata1, dir, retract, false)); ++ world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockposition3, iblockdata2, allowDesync ? list1.get(k) : iblockdata1, dir, retract, false)); + if (!allowDesync) { + world.setBlock(oldPos, Blocks.AIR.defaultBlockState(), 2 | 4 | 16 | 1024); // set air to prevent later physics updates from seeing this block + } @@ -82,12 +84,12 @@ diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBl index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java +++ b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java -@@ -0,0 +0,0 @@ public class PistonMovingBlockEntity extends BlockEntity implements TickableBloc - BlockState iblockdata = Block.updateFromNeighbourShapes(this.movedState, (LevelAccessor) this.level, this.worldPosition); - - if (iblockdata.isAir()) { -- this.level.setBlock(this.worldPosition, this.movedState, 84); -+ this.level.setBlock(this.worldPosition, this.movedState, com.destroystokyo.paper.PaperConfig.allowPistonDuplication ? 84 : (84 | 2)); // Paper - force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air - Block.updateOrDestroy(this.movedState, iblockdata, this.level, this.worldPosition, 3); +@@ -0,0 +0,0 @@ public class PistonMovingBlockEntity extends BlockEntity { + if (blockEntity.movedState != null && world.getBlockState(pos).is(Blocks.MOVING_PISTON)) { + BlockState blockState = Block.updateFromNeighbourShapes(blockEntity.movedState, world, pos); + if (blockState.isAir()) { +- world.setBlock(pos, blockEntity.movedState, 84); ++ world.setBlock(pos, blockEntity.movedState, com.destroystokyo.paper.PaperConfig.allowPistonDuplication ? 84 : (84 | 2)); // Paper - force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air + Block.updateOrDestroy(blockEntity.movedState, blockState, world, pos, 3); } else { - if (iblockdata.hasProperty(BlockStateProperties.WATERLOGGED) && (Boolean) iblockdata.getValue(BlockStateProperties.WATERLOGGED)) { + if (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)) { diff --git a/patches/server-remapped/Fix-sand-duping.patch b/patches/server/Fix-sand-duping.patch similarity index 94% rename from patches/server-remapped/Fix-sand-duping.patch rename to patches/server/Fix-sand-duping.patch index 733ef66a24..8a8245a06d 100644 --- a/patches/server-remapped/Fix-sand-duping.patch +++ b/patches/server/Fix-sand-duping.patch @@ -15,19 +15,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @Override public void tick() { + // Paper start - fix sand duping -+ if (this.removed) { ++ if (this.isRemoved()) { + return; + } + // Paper end - fix sand duping if (this.blockState.isAir()) { - this.remove(); + this.discard(); } else { @@ -0,0 +0,0 @@ public class FallingBlockEntity extends Entity { this.move(MoverType.SELF, this.getDeltaMovement()); + // Paper start - fix sand duping -+ if (this.removed) { ++ if (this.isRemoved()) { + return; + } + // Paper end - fix sand duping diff --git a/patches/server-remapped/Hide-sync-chunk-writes-behind-flag.patch b/patches/server/Hide-sync-chunk-writes-behind-flag.patch similarity index 100% rename from patches/server-remapped/Hide-sync-chunk-writes-behind-flag.patch rename to patches/server/Hide-sync-chunk-writes-behind-flag.patch diff --git a/patches/server-remapped/Improve-Legacy-Component-serialization-size.patch b/patches/server/Improve-Legacy-Component-serialization-size.patch similarity index 84% rename from patches/server-remapped/Improve-Legacy-Component-serialization-size.patch rename to patches/server/Improve-Legacy-Component-serialization-size.patch index df0b482e0a..1adb6079de 100644 --- a/patches/server-remapped/Improve-Legacy-Component-serialization-size.patch +++ b/patches/server/Improve-Legacy-Component-serialization-size.patch @@ -15,11 +15,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 private static final Pattern INCREMENTAL_PATTERN_KEEP_NEWLINES = Pattern.compile("(" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + "[0-9a-fk-orx])|((?:(?:https?):\\/\\/)?(?:[-\\w_\\.]{2,}\\.[a-z]{2,4}.*?(?=[\\.\\?!,;:]?(?:[" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + " ]|$))))", Pattern.CASE_INSENSITIVE); // ChatColor.b does not explicitly reset, its more of empty + private static final Style EMPTY = Style.EMPTY.withItalic(false); // Paper - OBFHELPER - private static final Style RESET = Style.EMPTY.withBold(false).withItalic(false).setUnderline(false).setStrikethrough(false).setRandom(false); + private static final Style RESET = Style.EMPTY.withBold(false).withItalic(false).withUnderlined(false).withStrikethrough(false).withObfuscated(false); private final List list = new ArrayList(); @@ -0,0 +0,0 @@ public final class CraftChatMessage { - Matcher matcher = (keepNewlines ? INCREMENTAL_PATTERN_KEEP_NEWLINES : INCREMENTAL_PATTERN).matcher(message); + Matcher matcher = (keepNewlines ? StringMessage.INCREMENTAL_PATTERN_KEEP_NEWLINES : StringMessage.INCREMENTAL_PATTERN).matcher(message); String match = null; boolean needsAdd = false; + boolean hasReset = false; // Paper @@ -30,7 +30,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 throw new AssertionError("Unexpected message format"); } } else { // Color resets formatting -- modifier = RESET.withColor(format); +- this.modifier = StringMessage.RESET.withColor(format); + // Paper start - improve legacy formatting + Style previous = modifier; + modifier = (!hasReset ? RESET : EMPTY).withColor(format); @@ -42,13 +42,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + modifier = modifier.withItalic(false); + } + if (previous.isObfuscated()) { -+ modifier = modifier.setRandom(false); ++ modifier = modifier.withObfuscated(false); + } + if (previous.isStrikethrough()) { -+ modifier = modifier.setStrikethrough(false); ++ modifier = modifier.withStrikethrough(false); + } + if (previous.isUnderlined()) { -+ modifier = modifier.setUnderline(false); ++ modifier = modifier.withUnderlined(false); + } + // Paper end } diff --git a/patches/server-remapped/Inventory-getHolder-method-without-block-snapshot.patch b/patches/server/Inventory-getHolder-method-without-block-snapshot.patch similarity index 66% rename from patches/server-remapped/Inventory-getHolder-method-without-block-snapshot.patch rename to patches/server/Inventory-getHolder-method-without-block-snapshot.patch index 0fa4bb9fbf..f540c971d5 100644 --- a/patches/server-remapped/Inventory-getHolder-method-without-block-snapshot.patch +++ b/patches/server/Inventory-getHolder-method-without-block-snapshot.patch @@ -8,28 +8,20 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java -@@ -0,0 +0,0 @@ import net.minecraft.world.inventory.PlayerEnderChestContainer; - import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; - import net.minecraft.world.level.block.entity.BarrelBlockEntity; - import net.minecraft.world.level.block.entity.BlastFurnaceBlockEntity; -+import net.minecraft.world.level.block.entity.BlockEntity; - import net.minecraft.world.level.block.entity.BrewingStandBlockEntity; - import net.minecraft.world.level.block.entity.DispenserBlockEntity; - import net.minecraft.world.level.block.entity.DropperBlockEntity; @@ -0,0 +0,0 @@ public class CraftInventory implements Inventory { - return inventory.getOwner(); + return this.inventory.getOwner(); } + // Paper start - getHolder without snapshot + @Override + public InventoryHolder getHolder(boolean useSnapshot) { -+ return inventory instanceof BlockEntity ? ((BlockEntity) inventory).getOwner(useSnapshot) : getHolder(); ++ return inventory instanceof net.minecraft.world.level.block.entity.BlockEntity ? ((net.minecraft.world.level.block.entity.BlockEntity) inventory).getOwner(useSnapshot) : getHolder(); + } + // Paper end + @Override public int getMaxStackSize() { - return inventory.getMaxStackSize(); + return this.inventory.getMaxStackSize(); diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java @@ -47,4 +39,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + @Override public Location getLocation() { - return getLeftSide().getLocation().add(getRightSide().getLocation()).multiply(0.5); + return this.getLeftSide().getLocation().add(this.getRightSide().getLocation()).multiply(0.5); diff --git a/patches/server/Optimize-WorldBorder-collision-checks-and-air.patch b/patches/server/Optimize-WorldBorder-collision-checks-and-air.patch new file mode 100644 index 0000000000..1f5b6c52a0 --- /dev/null +++ b/patches/server/Optimize-WorldBorder-collision-checks-and-air.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 10 May 2020 22:49:05 -0400 +Subject: [PATCH] Optimize WorldBorder collision checks and air + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -0,0 +0,0 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, n + AABB axisalignedbb = this.getBoundingBox(); + CollisionContext voxelshapecollision = CollisionContext.of(this); + VoxelShape voxelshape = this.level.getWorldBorder().getCollisionShape(); +- Stream stream = Shapes.joinIsNotEmpty(voxelshape, Shapes.create(axisalignedbb.deflate(1.0E-7D)), BooleanOp.AND) ? Stream.empty() : Stream.of(voxelshape); ++ Stream stream = !this.level.getWorldBorder().isWithinBounds(axisalignedbb) ? Stream.empty() : Stream.of(voxelshape); // Paper + Stream stream1 = this.level.getEntityCollisions(this, axisalignedbb.expandTowards(movement), (entity) -> { + return true; + }); +diff --git a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/level/CollisionSpliterator.java ++++ b/src/main/java/net/minecraft/world/level/CollisionSpliterator.java +@@ -0,0 +0,0 @@ public class CollisionSpliterator extends AbstractSpliterator { + WorldBorder worldBorder = this.collisionGetter.getWorldBorder(); + AABB aABB = this.source.getBoundingBox(); + if (!isBoxFullyWithinWorldBorder(worldBorder, aABB)) { +- VoxelShape voxelShape = worldBorder.getCollisionShape(); +- if (!isOutsideBorder(voxelShape, aABB) && isCloseToBorder(voxelShape, aABB)) { +- action.accept(voxelShape); ++ // Paper start ++ if (worldBorder.isWithinBounds(aABB.deflate(1.0E-7D)) && !worldBorder.isWithinBounds(aABB.inflate(1.0E-7D))) { ++ action.accept(worldBorder.asVoxelShape()); ++ // Paper end + return true; + } + } +diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java ++++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java +@@ -0,0 +0,0 @@ public final class Shapes { + mutableBlockPos.set(axisCycle, q, r, p); + BlockState blockState = world.getTypeIfLoaded(mutableBlockPos); // Paper + if (blockState == null) return 0.0D; // Paper +- if ((s != 1 || blockState.hasLargeCollisionShape()) && (s != 2 || blockState.is(Blocks.MOVING_PISTON))) { ++ if (!blockState.isAir() && (s != 1 || blockState.hasLargeCollisionShape()) && (s != 2 || blockState.is(Blocks.MOVING_PISTON))) { // Paper + initial = blockState.getCollisionShape(world, mutableBlockPos, context).collide(axis3, box.move((double)(-mutableBlockPos.getX()), (double)(-mutableBlockPos.getY()), (double)(-mutableBlockPos.getZ())), initial); + if (Math.abs(initial) < 1.0E-7D) { + return 0.0D; diff --git a/patches/server-remapped/Paper-dumpitem-command.patch b/patches/server/Paper-dumpitem-command.patch similarity index 64% rename from patches/server-remapped/Paper-dumpitem-command.patch rename to patches/server/Paper-dumpitem-command.patch index ddf312aaa0..fab9c8018f 100644 --- a/patches/server-remapped/Paper-dumpitem-command.patch +++ b/patches/server/Paper-dumpitem-command.patch @@ -9,14 +9,14 @@ diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/ index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/com/destroystokyo/paper/PaperCommand.java +++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java -@@ -0,0 +0,0 @@ import com.google.gson.JsonObject; - import com.google.gson.internal.Streams; - import com.google.gson.stream.JsonWriter; - import net.minecraft.core.BlockPos; -+import net.minecraft.nbt.CompoundTag; - import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; +@@ -0,0 +0,0 @@ import net.minecraft.server.level.ServerPlayer; + import net.minecraft.server.level.ThreadedLevelLightEngine; + import net.minecraft.world.entity.EntityType; + import net.minecraft.world.level.ChunkPos; ++import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MCUtil; + import org.apache.commons.lang3.tuple.MutablePair; @@ -0,0 +0,0 @@ import org.bukkit.command.CommandSender; import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftWorld; @@ -26,13 +26,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import org.bukkit.inventory.ItemStack; import java.io.File; - import java.io.FileOutputStream; + import java.time.LocalDateTime; @@ -0,0 +0,0 @@ import java.util.stream.Collectors; public class PaperCommand extends Command { private static final String BASE_PERM = "bukkit.command.paper."; -- private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "dumpwaiting", "syncloadinfo", "fixlight").build(); -+ private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "dumpwaiting", "syncloadinfo", "fixlight", "dumpitem").build(); +- private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight").build(); ++ private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "dumpitem").build(); public PaperCommand(String name) { super(name); @@ -47,17 +47,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 doDebug(sender, args); break; @@ -0,0 +0,0 @@ public class PaperCommand extends Command { - return true; - } + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Paper config reload complete."); + } + private void doDumpItem(CommandSender sender) { + ItemStack itemInHand = ((CraftPlayer) sender).getItemInHand(); + net.minecraft.world.item.ItemStack itemStack = CraftItemStack.asNMSCopy(itemInHand); -+ CompoundTag tag = itemStack.getTag(); ++ net.minecraft.nbt.CompoundTag tag = itemStack.getTag(); + if (tag != null) { -+ String nbt = org.bukkit.craftbukkit.util.CraftChatMessage.fromComponent(tag.getNbtPrettyComponent()); -+ Bukkit.getConsoleSender().sendMessage(nbt); -+ sender.sendMessage(nbt); ++ net.kyori.adventure.text.Component nbtComponent = io.papermc.paper.adventure.PaperAdventure.asAdventure(net.minecraft.nbt.NbtUtils.toPrettyComponent(tag)); ++ Bukkit.getConsoleSender().sendMessage(nbtComponent); ++ sender.sendMessage(nbtComponent); + } else { + sender.sendMessage("Item does not have NBT"); + } @@ -66,15 +66,3 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 private void doFixLight(CommandSender sender, String[] args) { if (!(sender instanceof Player)) { sender.sendMessage("Only players can use this command"); -diff --git a/src/main/java/net/minecraft/nbt/Tag.java b/src/main/java/net/minecraft/nbt/Tag.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/net/minecraft/nbt/Tag.java -+++ b/src/main/java/net/minecraft/nbt/Tag.java -@@ -0,0 +0,0 @@ public interface Tag { - return this.toString(); - } - -+ default Component getNbtPrettyComponent() { return this.getPrettyDisplay(); } // Paper - OBFHELPER - default Component getPrettyDisplay() { - return this.getPrettyDisplay("", 0); - } diff --git a/patches/server-remapped/Prevent-position-desync-in-playerconnection-causing-.patch b/patches/server/Prevent-position-desync-in-playerconnection-causing-.patch similarity index 95% rename from patches/server-remapped/Prevent-position-desync-in-playerconnection-causing-.patch rename to patches/server/Prevent-position-desync-in-playerconnection-causing-.patch index db78093ca1..34a996e3a4 100644 --- a/patches/server-remapped/Prevent-position-desync-in-playerconnection-causing-.patch +++ b/patches/server/Prevent-position-desync-in-playerconnection-causing-.patch @@ -17,7 +17,7 @@ diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListener index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener { +@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser this.player.move(MoverType.PLAYER, new Vec3(d7, d8, d9)); this.player.setOnGround(packet.isOnGround()); // CraftBukkit - SPIGOT-5810, SPIGOT-5835: reset by this.player.move @@ -28,4 +28,4 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + // Paper end - prevent position desync double d12 = d8; - d7 = d4 - this.player.getX(); + d7 = d0 - this.player.getX(); diff --git a/patches/server-remapped/Support-old-UUID-format-for-NBT.patch b/patches/server/Support-old-UUID-format-for-NBT.patch similarity index 60% rename from patches/server-remapped/Support-old-UUID-format-for-NBT.patch rename to patches/server/Support-old-UUID-format-for-NBT.patch index cb7c77a4a9..264d3aaff7 100644 --- a/patches/server-remapped/Support-old-UUID-format-for-NBT.patch +++ b/patches/server/Support-old-UUID-format-for-NBT.patch @@ -25,8 +25,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } @@ -0,0 +0,0 @@ public class CompoundTag implements Tag { + * You must use {@link #hasUUID(String)} before or else it will throw an NPE. */ - public UUID getUUID(String prefix) { return getUUID(prefix); } // Paper - OBFHELPER public UUID getUUID(String key) { + // Paper start - support old format + if (!contains(key, 11) && this.contains(key + "Most", 99) && this.contains(key + "Least", 99)) { @@ -36,29 +36,45 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 return NbtUtils.loadUUID(this.get(key)); } - public final boolean hasUUID(String s) { return this.hasUUID(s); } // Paper - OBFHELPER public boolean hasUUID(String key) { + // Paper start - support old format + if (this.contains(key + "Most", 99) && this.contains(key + "Least", 99)) { + return true; + } + // Paper end - Tag nbtbase = this.get(key); - - return nbtbase != null && nbtbase.getType() == IntArrayTag.TYPE && ((IntArrayTag) nbtbase).getAsIntArray().length == 4; + Tag tag = this.get(key); + return tag != null && tag.getType() == IntArrayTag.TYPE && ((IntArrayTag)tag).getAsIntArray().length == 4; + } diff --git a/src/main/java/net/minecraft/nbt/NbtUtils.java b/src/main/java/net/minecraft/nbt/NbtUtils.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/nbt/NbtUtils.java +++ b/src/main/java/net/minecraft/nbt/NbtUtils.java +@@ -0,0 +0,0 @@ import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + + public final class NbtUtils { +- private static final Comparator YXZ_LISTTAG_INT_COMPARATOR = Comparator.comparingInt((listTag) -> { ++ private static final Comparator YXZ_LISTTAG_INT_COMPARATOR = Comparator.comparingInt((listTag) -> { // Paper - decompile fix + return listTag.getInt(1); + }).thenComparingInt((listTag) -> { + return listTag.getInt(0); + }).thenComparingInt((listTag) -> { + return listTag.getInt(2); + }); +- private static final Comparator YXZ_LISTTAG_DOUBLE_COMPARATOR = Comparator.comparingDouble((listTag) -> { ++ private static final Comparator YXZ_LISTTAG_DOUBLE_COMPARATOR = Comparator.comparingDouble((listTag) -> { // Paper - decompile fix + return listTag.getDouble(1); + }).thenComparingDouble((listTag) -> { + return listTag.getDouble(0); @@ -0,0 +0,0 @@ public final class NbtUtils { - s = tag.getString("Name"); + string = compound.getString("Name"); } + // Paper start - support string UUID's -+ if (tag.contains("Id", 8)) { -+ uuid = UUID.fromString(tag.getString("Id")); ++ if (compound.contains("Id", 8)) { ++ uUID = UUID.fromString(compound.getString("Id")); + } + // Paper end - if (tag.hasUUID("Id")) { - uuid = tag.getUUID("Id"); + if (compound.hasUUID("Id")) { + uUID = compound.getUUID("Id"); }