From 3d455ff2d59d2e08a1675ce84250e4883a19525c Mon Sep 17 00:00:00 2001 From: BillyGalbreath Date: Mon, 28 Dec 2020 01:21:49 -0600 Subject: [PATCH] Updated Upstream (Paper & Tuinity) Upstream has released updates that appear to apply and compile correctly Paper Changes: e4633ca27 [Auto] Updated Upstream (Bukkit/CraftBukkit) a23be44da [Auto] Updated Upstream (CraftBukkit) e40ea889d [CI-SKIP] Deprecate the view distance APIs on Player (#4945) d9fd54e16 Do not crash from invalid ingredient lists in VillagerAcquireTradeEvent 24388381a Fix lead duplication Tuinity Changes: 09423b6 Update to Starlight 0.0.2-RC3 f06591a Update Upstream (Paper) d2aa70f Revert special block lookup for chunksection --- Paper | 2 +- patches/api/0007-AFK-API.patch | 4 +- .../api/0018-Player-invulnerabilities.patch | 4 +- ...oc-warnings-missing-param-and-return.patch | 6 +- .../server/0001-Tuinity-Server-Changes.patch | 3434 +++++++++++------ patches/server/0009-AFK-API.patch | 10 +- .../0012-LivingEntity-safeFallDistance.patch | 2 +- patches/server/0017-EntityMoveEvent.patch | 4 +- .../0018-Player-invulnerabilities.patch | 10 +- patches/server/0027-Giants-AI-settings.patch | 4 +- .../0029-Zombie-horse-naturally-spawn.patch | 4 +- .../server/0039-Cat-spawning-options.patch | 4 +- patches/server/0041-Cows-eat-mushrooms.patch | 6 +- .../server/0048-Signs-allow-color-codes.patch | 4 +- .../server/0050-Controllable-Minecarts.patch | 6 +- ...able-loot-drops-on-death-by-cramming.patch | 2 +- ...052-Players-should-not-cram-to-death.patch | 4 +- ...0055-Fix-the-dead-lagging-the-server.patch | 6 +- ...0064-Villagers-follow-emerald-blocks.patch | 2 +- ...Projectile-load-save-limit-per-chunk.patch | 6 +- .../0071-Add-canSaveToDisk-to-Entity.patch | 6 +- ...0072-Configurable-void-damage-height.patch | 2 +- .../0079-Implement-elytra-settings.patch | 2 +- .../server/0080-Item-entity-immunities.patch | 6 +- ...ed-to-crystals-and-crystals-shoot-ph.patch | 4 +- ...leport-to-spawn-if-outside-world-bor.patch | 6 +- .../0097-Totems-work-in-inventory.patch | 2 +- .../0098-Fix-death-message-colors.patch | 4 +- .../server/0106-Add-no-tick-block-list.patch | 6 +- ...Stop-squids-floating-on-top-of-water.patch | 4 +- patches/server/0110-Ridables.patch | 24 +- ...ing-obsidian-valid-for-portal-frames.patch | 4 +- ...tities-can-use-portals-configuration.patch | 6 +- ...ggling-special-MobSpawners-per-world.patch | 2 +- .../0126-Configurable-daylight-cycle.patch | 6 +- ...30-Add-tablist-suffix-option-for-afk.patch | 4 +- ...mes-from-item-forms-of-entities-to-e.patch | 2 +- .../0146-Add-boat-fall-damage-config.patch | 4 +- ...149-PaperPR-Apply-advancements-async.patch | 4 +- .../0153-Lobotomize-stuck-villagers.patch | 2 +- ...ble-chance-for-wolves-to-spawn-rabid.patch | 2 +- 41 files changed, 2405 insertions(+), 1221 deletions(-) diff --git a/Paper b/Paper index 0bdfb0158..e4633ca27 160000 --- a/Paper +++ b/Paper @@ -1 +1 @@ -Subproject commit 0bdfb0158975de82c1089a744fe26e11c9b9bd52 +Subproject commit e4633ca2777aad0bd570733a7623f5e1107fbb04 diff --git a/patches/api/0007-AFK-API.patch b/patches/api/0007-AFK-API.patch index a0a68932c..22b443232 100644 --- a/patches/api/0007-AFK-API.patch +++ b/patches/api/0007-AFK-API.patch @@ -81,10 +81,10 @@ index 0000000000000000000000000000000000000000..0c8b3e5e4ba412624357ea5662a78862 + } +} diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index a23c08c48ec627147d94ab4bf4fdf4dae1edeaca..bf4699c405447d07e9a4b70522c7a7c944677f74 100644 +index f7fd13dc6056817819c1dbffcaf19c25b95fe2c0..250b434af848546fa3a6ffd748f03a910095b33d 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java -@@ -1925,4 +1925,25 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -1933,4 +1933,25 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM @Override Spigot spigot(); // Spigot end diff --git a/patches/api/0018-Player-invulnerabilities.patch b/patches/api/0018-Player-invulnerabilities.patch index 5b493b1e3..f1a07fc14 100644 --- a/patches/api/0018-Player-invulnerabilities.patch +++ b/patches/api/0018-Player-invulnerabilities.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Player invulnerabilities diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index bf4699c405447d07e9a4b70522c7a7c944677f74..18b5f84a1d7567e17d8baa9abb889a1f7cc7f5b8 100644 +index 250b434af848546fa3a6ffd748f03a910095b33d..e398da35bef48b5846d8c66df75b1df11b43e382 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java -@@ -1945,5 +1945,26 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -1953,5 +1953,26 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM * Reset the idle timer back to 0 */ void resetIdleTimer(); diff --git a/patches/api/0035-Fix-javadoc-warnings-missing-param-and-return.patch b/patches/api/0035-Fix-javadoc-warnings-missing-param-and-return.patch index 454eb431e..728a7b11f 100644 --- a/patches/api/0035-Fix-javadoc-warnings-missing-param-and-return.patch +++ b/patches/api/0035-Fix-javadoc-warnings-missing-param-and-return.patch @@ -1015,7 +1015,7 @@ index a6a7429ed2e1eefb2b12b7480ed74fcc3963a864..e8027e1d505dda6effbb1698550016e8 NORMAL(false), diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 18b5f84a1d7567e17d8baa9abb889a1f7cc7f5b8..721c6fbe5bdebf5e490af9956c2cef4240d691fa 100644 +index e398da35bef48b5846d8c66df75b1df11b43e382..8da3ce6d1ba6911cb1a4be2e425266bd4760c052 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -1433,7 +1433,6 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM @@ -1066,7 +1066,7 @@ index 18b5f84a1d7567e17d8baa9abb889a1f7cc7f5b8..721c6fbe5bdebf5e490af9956c2cef42 */ public void spawnParticle(@NotNull Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, @Nullable T data); -@@ -1774,6 +1768,8 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -1782,6 +1776,8 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM void resetCooldown(); /** @@ -1075,7 +1075,7 @@ index 18b5f84a1d7567e17d8baa9abb889a1f7cc7f5b8..721c6fbe5bdebf5e490af9956c2cef42 * @return the client option value of the player */ @NotNull -@@ -1794,6 +1790,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM +@@ -1802,6 +1798,9 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM // Paper end // Spigot start diff --git a/patches/server/0001-Tuinity-Server-Changes.patch b/patches/server/0001-Tuinity-Server-Changes.patch index 6da51a089..ba9199e8e 100644 --- a/patches/server/0001-Tuinity-Server-Changes.patch +++ b/patches/server/0001-Tuinity-Server-Changes.patch @@ -362,6 +362,21 @@ Copy passenger list in enderTeleportTo Fixes https://github.com/Spottedleaf/Tuinity/issues/208 +Revert MC-4 fix + +When messing around with collisions, I ran into problems where +entity position was off by ULP and that caused clipping problems. +Now, the collision epsilon is 1.0e-7 to account for those errors. + +But this patch is going to cause problems on the order of 1.0e-4. + +I do not want to deal with clipping problems. The very fact it works +shows it's causing the clipping to occur serverside. + +Prevent light queue overfill when no players are online + +block changes don't queue light updates (and they shouldn't) + Rewrite the light engine The standard vanilla light engine is plagued by @@ -1492,16 +1507,17 @@ index 0000000000000000000000000000000000000000..cae06962d80cdd00962236891472ba81 \ No newline at end of file diff --git a/src/main/java/com/tuinity/tuinity/chunk/light/BlockStarLightEngine.java b/src/main/java/com/tuinity/tuinity/chunk/light/BlockStarLightEngine.java new file mode 100644 -index 0000000000000000000000000000000000000000..e7df9dc4b82c08aafc0a0bb076d9027c1f7758e2 +index 0000000000000000000000000000000000000000..d7fa3eb57af562d02e821f107387d470e1de4f73 --- /dev/null +++ b/src/main/java/com/tuinity/tuinity/chunk/light/BlockStarLightEngine.java -@@ -0,0 +1,155 @@ +@@ -0,0 +1,164 @@ +package com.tuinity.tuinity.chunk.light; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkSection; +import net.minecraft.server.ChunkStatus; ++import net.minecraft.server.DataPaletteBlock; +import net.minecraft.server.IBlockData; +import net.minecraft.server.IChunkAccess; +import net.minecraft.server.ILightAccess; @@ -1513,8 +1529,8 @@ index 0000000000000000000000000000000000000000..e7df9dc4b82c08aafc0a0bb076d9027c + +public final class BlockStarLightEngine extends StarLightEngine { + -+ public BlockStarLightEngine() { -+ super(false); ++ public BlockStarLightEngine(final boolean isClientSide) { ++ super(false, isClientSide); + } + + @Override @@ -1529,10 +1545,19 @@ index 0000000000000000000000000000000000000000..e7df9dc4b82c08aafc0a0bb076d9027c + + @Override + protected boolean canUseChunk(final IChunkAccess chunk) { -+ return chunk.getChunkStatus().isAtLeastStatus(ChunkStatus.LIGHT) && chunk.isLit(); ++ return chunk.getChunkStatus().isAtLeastStatus(ChunkStatus.LIGHT) && (this.isClientSide || chunk.isLit()); + } + + @Override ++ protected boolean[][] getEmptinessMap(IChunkAccess chunk) { ++ return null; ++ } ++ ++ @Override ++ protected void handleEmptySectionChanges(final ILightAccess lightAccess, final IChunkAccess chunk, ++ final Boolean[] emptinessChanges, final boolean unlit) {} ++ ++ @Override + protected final void checkBlock(final int worldX, final int worldY, final int worldZ) { + // blocks can change opacity + // blocks can change emitted light @@ -1542,33 +1567,34 @@ index 0000000000000000000000000000000000000000..e7df9dc4b82c08aafc0a0bb076d9027c + final int emittedMask = this.emittedLightMask; + + final int currentLevel = this.getLightLevel(worldX, worldY, worldZ); -+ final IBlockData blockData = this.getBlockData(worldX, worldY, worldZ); -+ final int emittedLevel = blockData.getEmittedLight() & emittedMask; ++ final IBlockData blockState = this.getBlockState(worldX, worldY, worldZ); ++ final int emittedLevel = blockState.getEmittedLight() & emittedMask; + + this.setLightLevel(worldX, worldY, worldZ, emittedLevel); + // this accounts for change in emitted light that would cause an increase + if (emittedLevel != 0) { -+ this.increaseQueue[this.increaseQueueInitialLength++] = (worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) | -+ emittedLevel << (6 + 6 + 9) | -+ ((Direction.POSITIVE_X.ordinal() | 8) << (6 + 6 + 9 + 4)) | -+ (blockData.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0); ++ this.increaseQueue[this.increaseQueueInitialLength++] = ++ ((worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (emittedLevel & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (blockState.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0); + } + // this also accounts for a change in emitted light that would cause a decrease + // this also accounts for the change of direction of propagation (i.e old block was full transparent, new block is full opaque or vice versa) + // as it checks all neighbours (even if current level is 0) -+ this.decreaseQueue[this.decreaseQueueInitialLength++] = (worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) | -+ currentLevel << (6 + 6 + 9) | -+ ((Direction.POSITIVE_X.ordinal() | 8) << (6 + 6 + 9 + 4)); -+ // always keep sided transparent false here, new block might be conditionally transparent which would -+ // prevent us from decreasing sources in the directions where the new block is opaque -+ // if it turns out we were wrong to de-propagate the source, the re-propagate logic WILL always -+ // catch that and fix it. ++ this.decreaseQueue[this.decreaseQueueInitialLength++] = ++ ((worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (currentLevel & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)); ++ // always keep sided transparent false here, new block might be conditionally transparent which would ++ // prevent us from decreasing sources in the directions where the new block is opaque ++ // if it turns out we were wrong to de-propagate the source, the re-propagate logic WILL always ++ // catch that and fix it. + // re-propagating neighbours (done by the decrease queue) will also account for opacity changes in this block + } + + @Override -+ protected void propagateBlockChanges(final ILightAccess lightAccess, final IChunkAccess atChunk, -+ final Set positions) { ++ protected void propagateBlockChanges(final ILightAccess lightAccess, final IChunkAccess atChunk, final Set positions) { + for (final BlockPosition pos : positions) { + this.checkBlock(pos.getX(), pos.getY(), pos.getZ()); + } @@ -1591,20 +1617,17 @@ index 0000000000000000000000000000000000000000..e7df9dc4b82c08aafc0a0bb076d9027c + // no sources in empty sections + continue; + } -+ final ChunkSection section = sections[sectionY]; ++ final DataPaletteBlock section = sections[sectionY].blockIds; ++ final int offY = sectionY << 4; + -+ for (int localY = 0; localY <= 15; ++localY) { -+ final int realY = localY | (sectionY << 4); -+ for (int localZ = 0; localZ <= 15; ++localZ) { -+ for (int localX = 0; localX <= 15; ++localX) { -+ final IBlockData blockData = section.getType(localX, localY, localZ); -+ if (blockData.getEmittedLight() <= 0) { -+ continue; -+ } -+ -+ sources.add(new BlockPosition(offX + localX, realY, offZ + localZ)); -+ } ++ for (int index = 0; index < (16 * 16 * 16); ++index) { ++ final IBlockData state = section.rawGet(index); ++ if (state.getEmittedLight() <= 0) { ++ continue; + } ++ ++ // index = x | (z << 4) | (y << 8) ++ sources.add(new BlockPosition(offX | (index & 15), offY | (index >>> 8), offZ | ((index >>> 4) & 15))); + } + } + @@ -1620,18 +1643,19 @@ index 0000000000000000000000000000000000000000..e7df9dc4b82c08aafc0a0bb076d9027c + final int emittedMask = this.emittedLightMask; + for (final Iterator positions = this.getSources(chunk); positions.hasNext();) { + final BlockPosition pos = positions.next(); -+ final IBlockData blockData = this.getBlockData(pos.getX(), pos.getY(), pos.getZ()); -+ final int emittedLight = blockData.getEmittedLight() & emittedMask; ++ final IBlockData blockState = this.getBlockState(pos.getX(), pos.getY(), pos.getZ()); ++ final int emittedLight = blockState.getEmittedLight() & emittedMask; + + if (emittedLight <= this.getLightLevel(pos.getX(), pos.getY(), pos.getZ())) { + // some other source is brighter + continue; + } + -+ this.increaseQueue[this.increaseQueueInitialLength++] = (pos.getX() + (pos.getZ() << 6) + (pos.getY() << (6 + 6)) + this.coordinateOffset) | -+ (emittedLight) << (6 + 6 + 9) | -+ ((Direction.POSITIVE_X.ordinal() | 8) << (6 + 6 + 9 + 4)) | -+ (blockData.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0); ++ this.increaseQueue[this.increaseQueueInitialLength++] = ++ ((pos.getX() + (pos.getZ() << 6) + (pos.getY() << (6 + 6)) + this.coordinateOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (emittedLight & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (blockState.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0); + + + // propagation wont set this for us @@ -1643,7 +1667,7 @@ index 0000000000000000000000000000000000000000..e7df9dc4b82c08aafc0a0bb076d9027c + this.performLightIncrease(lightAccess); + + // verify neighbour edges -+ this.checkChunkEdges(lightAccess, chunk); ++ this.checkChunkEdges(lightAccess, chunk, -1, 16); + } else { + this.propagateNeighbourLevels(lightAccess, chunk, -1, 16); + @@ -1653,10 +1677,10 @@ index 0000000000000000000000000000000000000000..e7df9dc4b82c08aafc0a0bb076d9027c +} diff --git a/src/main/java/com/tuinity/tuinity/chunk/light/SWMRNibbleArray.java b/src/main/java/com/tuinity/tuinity/chunk/light/SWMRNibbleArray.java new file mode 100644 -index 0000000000000000000000000000000000000000..6cae16cc32c49d1787b18f3f51788fe4743113bf +index 0000000000000000000000000000000000000000..9910dc9f1a087f5baf404a5b8ebb5a9f1f97f3f4 --- /dev/null +++ b/src/main/java/com/tuinity/tuinity/chunk/light/SWMRNibbleArray.java -@@ -0,0 +1,196 @@ +@@ -0,0 +1,317 @@ +package com.tuinity.tuinity.chunk.light; + +import net.minecraft.server.NibbleArray; @@ -1666,6 +1690,20 @@ index 0000000000000000000000000000000000000000..6cae16cc32c49d1787b18f3f51788fe4 +// SWMR -> Single Writer Multi Reader Nibble Array +public final class SWMRNibbleArray { + ++ /* ++ * Null nibble - nibble does not exist, and should not be written to. Just like vanilla - null ++ * nibbles are always 0 - and they are never written to directly. Only initialised/uninitialised ++ * nibbles can be written to. ++ * ++ * Uninitialised nibble - They are all 0, but the backing array isn't initialised. ++ * ++ * Initialised nibble - Has light data. ++ */ ++ ++ protected static final int INIT_STATE_NULL = 0; // null ++ protected static final int INIT_STATE_UNINIT = 1; // uninitialised ++ protected static final int INIT_STATE_INIT = 2; // initialised ++ + public static final int ARRAY_SIZE = 16 * 16 * 16 / (8/4); // blocks / bytes per block + protected static final byte[] FULL_LIT = new byte[ARRAY_SIZE]; + static { @@ -1687,132 +1725,234 @@ index 0000000000000000000000000000000000000000..6cae16cc32c49d1787b18f3f51788fe4 + WORKING_BYTES_POOL.get().addFirst(bytes); + } + -+ protected byte[] workingBytes; -+ protected byte[] visibleBytes; -+ protected final int defaultNullValue; -+ private boolean isNullNibble; ++ protected int stateUpdating; ++ protected volatile int stateVisible; + -+ public SWMRNibbleArray(final boolean isNullNibble, final int defaultNullValue) { -+ this(null, defaultNullValue); -+ this.isNullNibble = isNullNibble; -+ } ++ protected byte[] storageUpdating; ++ protected boolean updatingDirty; // only returns whether storageUpdating is dirty ++ protected byte[] storageVisible; + + public SWMRNibbleArray() { -+ this(null, 0); // lazy init ++ this(null, false); // lazy init + } + + public SWMRNibbleArray(final byte[] bytes) { -+ this(bytes, 0); ++ this(bytes, false); + } + -+ protected SWMRNibbleArray(final byte[] bytes, final int defaultNullValue) { ++ public SWMRNibbleArray(final byte[] bytes, final boolean isNullNibble) { + if (bytes != null && bytes.length != ARRAY_SIZE) { + throw new IllegalArgumentException(); + } -+ this.defaultNullValue = defaultNullValue; -+ this.visibleBytes = bytes != null ? bytes.clone() : null; ++ this.stateVisible = this.stateUpdating = bytes == null ? (isNullNibble ? INIT_STATE_NULL : INIT_STATE_UNINIT) : INIT_STATE_INIT; ++ this.storageUpdating = this.storageVisible = bytes; + } + -+ public boolean isDirty() { -+ return this.workingBytes != null; -+ } ++ // operation type: visible ++ public boolean isAllZero() { ++ final byte[] bytes = this.storageVisible; + -+ public boolean isNullNibbleUpdating() { -+ return this.workingBytes == null && this.isNullNibble; -+ } -+ -+ public boolean isNullNibbleVisible() { -+ synchronized (this) { -+ return this.isNullNibble; ++ if (this.storageVisible == null) { ++ return true; + } -+ } -+ -+ public void markNonNull() { -+ synchronized (this) { -+ this.isNullNibble = false; -+ } -+ } -+ -+ public boolean isInitialisedUpdating() { -+ return this.workingBytes != null || this.visibleBytes != null; -+ } -+ -+ public boolean isInitialisedVisible() { -+ synchronized (this) { -+ return this.visibleBytes != null; -+ } -+ } -+ -+ public void initialiseWorking() { -+ if (this.workingBytes != null) { -+ return; -+ } -+ final byte[] working = allocateBytes(); -+ this.copyIntoImpl(working, 0); -+ this.workingBytes = working; -+ } -+ -+ public void copyFrom(final byte[] src, final int off) { -+ if (this.workingBytes == null) { -+ this.workingBytes = allocateBytes(); -+ } -+ System.arraycopy(src, off, this.workingBytes, 0, ARRAY_SIZE); -+ } -+ -+ public boolean updateVisible() { -+ if (this.workingBytes == null) { -+ return false; -+ -+ } -+ final byte[] oldVisible = this.visibleBytes; + + synchronized (this) { -+ this.isNullNibble = false; ++ for (int i = 0; i < (ARRAY_SIZE >>> 4); ++i) { ++ byte whole = bytes[i << 4]; + -+ this.visibleBytes = this.workingBytes; -+ this.workingBytes = null; -+ } ++ for (int k = 1; k < (1 << 4); ++k) { ++ whole |= bytes[(i << 4) | k]; ++ } + -+ if (oldVisible != null) { -+ freeBytes(oldVisible); ++ if (whole != 0) { ++ return false; ++ } ++ } + } + + return true; + } + -+ public void copyInto(final byte[] bytes, final int off) { -+ synchronized (this) { -+ this.copyIntoImpl(bytes, off); ++ // operation type: updating on src, updating on other ++ public void extrudeLower(final SWMRNibbleArray other) { ++ if (other.stateUpdating == INIT_STATE_NULL) { ++ throw new IllegalArgumentException(); ++ } ++ ++ if (other.storageUpdating == null) { ++ this.setUninitialised(); ++ return; ++ } ++ ++ final byte[] src = other.storageUpdating; ++ final byte[] into; ++ ++ if (this.storageUpdating != null) { ++ into = this.storageUpdating; ++ } else { ++ this.storageUpdating = into = allocateBytes(); ++ this.stateUpdating = INIT_STATE_INIT; ++ } ++ this.updatingDirty = true; ++ ++ final int start = 0; ++ final int end = (15 | (15 << 4)) >>> 1; ++ ++ /* x | (z << 4) | (y << 8) */ ++ for (int y = 0; y <= 15; ++y) { ++ System.arraycopy(src, start, into, y << (8 - 1), end - start + 1); + } + } + -+ protected void copyIntoImpl(final byte[] bytes, final int off) { -+ if (this.visibleBytes != null) { -+ System.arraycopy(this.visibleBytes, 0, bytes, off, ARRAY_SIZE); ++ // operation type: updating ++ public void setFull() { ++ this.stateUpdating = INIT_STATE_INIT; ++ Arrays.fill(this.storageUpdating == null || !this.updatingDirty ? this.storageUpdating = allocateBytes() : this.storageUpdating, (byte)-1); ++ this.updatingDirty = true; ++ } ++ ++ // operation type: updating ++ public void setZero() { ++ this.stateUpdating = INIT_STATE_INIT; ++ Arrays.fill(this.storageUpdating == null || !this.updatingDirty ? this.storageUpdating = allocateBytes() : this.storageUpdating, (byte)0); ++ this.updatingDirty = true; ++ } ++ ++ // operation type: updating ++ public void setNonNull() { ++ if (this.stateUpdating != INIT_STATE_NULL) { ++ return; ++ } ++ this.stateUpdating = INIT_STATE_UNINIT; ++ } ++ ++ // operation type: updating ++ public void setNull() { ++ this.stateUpdating = INIT_STATE_NULL; ++ if (this.updatingDirty && this.storageUpdating != null) { ++ freeBytes(this.storageUpdating); ++ } ++ this.storageUpdating = null; ++ this.updatingDirty = false; ++ } ++ ++ // operation type: updating ++ public void setUninitialised() { ++ this.stateUpdating = INIT_STATE_UNINIT; ++ if (this.storageUpdating != null && this.updatingDirty) { ++ freeBytes(this.storageUpdating); ++ } ++ this.storageUpdating = null; ++ this.updatingDirty = false; ++ } ++ ++ // operation type: updating ++ public boolean isDirty() { ++ return this.stateUpdating != this.stateVisible || this.updatingDirty; ++ } ++ ++ // operation type: updating ++ public boolean isNullNibbleUpdating() { ++ return this.stateUpdating == INIT_STATE_NULL; ++ } ++ ++ // operation type: visible ++ public boolean isNullNibbleVisible() { ++ return this.stateVisible == INIT_STATE_NULL; ++ } ++ ++ // opeartion type: updating ++ public boolean isUninitialisedUpdating() { ++ return this.stateUpdating == INIT_STATE_UNINIT; ++ } ++ ++ // operation type: visible ++ public boolean isUninitialisedVisible() { ++ return this.stateVisible == INIT_STATE_UNINIT; ++ } ++ ++ // operation type: updating ++ public boolean isInitialisedUpdating() { ++ return this.stateUpdating == INIT_STATE_INIT; ++ } ++ ++ // operation type: visible ++ public boolean isInitialisedVisible() { ++ return this.stateVisible == INIT_STATE_INIT; ++ } ++ ++ // operation type: updating ++ protected void swapUpdatingAndMarkDirty() { ++ if (this.updatingDirty) { ++ return; ++ } ++ ++ if (this.storageUpdating == null) { ++ this.storageUpdating = allocateBytes(); ++ Arrays.fill(this.storageUpdating, (byte)0); + } else { -+ if (this.isNullNibble && this.defaultNullValue != 0) { -+ Arrays.fill(bytes, off, off + ARRAY_SIZE, (byte)(this.defaultNullValue | (this.defaultNullValue << 4))); ++ System.arraycopy(this.storageUpdating, 0, this.storageUpdating = allocateBytes(), 0, ARRAY_SIZE); ++ } ++ ++ this.stateUpdating = INIT_STATE_INIT; ++ this.updatingDirty = true; ++ } ++ ++ // operation type: updating ++ public boolean updateVisible() { ++ if (!this.isDirty()) { ++ return false; ++ } ++ ++ synchronized (this) { ++ if (this.stateUpdating == INIT_STATE_NULL || this.stateUpdating == INIT_STATE_UNINIT) { ++ this.storageVisible = null; + } else { -+ Arrays.fill(bytes, off, off + ARRAY_SIZE, (byte)0); ++ if (this.storageVisible == null) { ++ this.storageVisible = this.storageUpdating.clone(); ++ } else { ++ System.arraycopy(this.storageUpdating, 0, this.storageVisible, 0, ARRAY_SIZE); ++ } ++ ++ freeBytes(this.storageUpdating); ++ this.storageUpdating = this.storageVisible; ++ } ++ this.updatingDirty = false; ++ this.stateVisible = this.stateUpdating; ++ } ++ ++ return true; ++ } ++ ++ // operation type: visible ++ public NibbleArray toVanillaNibble() { ++ synchronized (this) { ++ switch (this.stateVisible) { ++ case INIT_STATE_NULL: ++ return null; ++ case INIT_STATE_UNINIT: ++ return new NibbleArray(); ++ case INIT_STATE_INIT: ++ return new NibbleArray(this.storageVisible.clone()); ++ default: ++ throw new IllegalStateException(); + } + } + } + -+ public NibbleArray asNibble() { -+ synchronized (this) { -+ return this.visibleBytes == null ? (this.isNullNibble ? null : new NibbleArray()) : new NibbleArray(this.visibleBytes.clone()); -+ } -+ } ++ /* x | (z << 4) | (y << 8) */ + ++ // operation type: updating + public int getUpdating(final int x, final int y, final int z) { + return this.getUpdating((x & 15) | ((z & 15) << 4) | ((y & 15) << 8)); + } + ++ // operation type: updating + public int getUpdating(final int index) { + // indices range from 0 -> 4096 -+ byte[] bytes = this.workingBytes == null ? this.visibleBytes : this.workingBytes; ++ final byte[] bytes = this.storageUpdating; + if (bytes == null) { -+ return this.isNullNibble ? this.defaultNullValue : 0; ++ return 0; + } + final byte value = bytes[index >>> 1]; + @@ -1821,17 +1961,20 @@ index 0000000000000000000000000000000000000000..6cae16cc32c49d1787b18f3f51788fe4 + return ((value >>> ((index & 1) << 2)) & 0xF); + } + ++ // operation type: visible + public int getVisible(final int x, final int y, final int z) { + return this.getVisible((x & 15) | ((z & 15) << 4) | ((y & 15) << 8)); + } + ++ // operation type: visible + public int getVisible(final int index) { + synchronized (this) { + // indices range from 0 -> 4096 -+ if (this.visibleBytes == null) { -+ return this.isNullNibble ? this.defaultNullValue : 0; ++ final byte[] visibleBytes = this.storageVisible; ++ if (visibleBytes == null) { ++ return 0; + } -+ final byte value = this.visibleBytes[index >>> 1]; ++ final byte value = visibleBytes[index >>> 1]; + + // if we are an even index, we want lower 4 bits + // if we are an odd index, we want upper 4 bits @@ -1839,28 +1982,31 @@ index 0000000000000000000000000000000000000000..6cae16cc32c49d1787b18f3f51788fe4 + } + } + ++ // operation type: updating + public void set(final int x, final int y, final int z, final int value) { + this.set((x & 15) | ((z & 15) << 4) | ((y & 15) << 8), value); + } + ++ // operation type: updating + public void set(final int index, final int value) { -+ if (this.workingBytes == null) { -+ this.initialiseWorking(); ++ if (!this.updatingDirty) { ++ this.swapUpdatingAndMarkDirty(); + } + final int shift = (index & 1) << 2; + final int i = index >>> 1; + -+ this.workingBytes[i] = (byte)((this.workingBytes[i] & (0xF0 >>> shift)) | (value << shift)); ++ this.storageUpdating[i] = (byte)((this.storageUpdating[i] & (0xF0 >>> shift)) | (value << shift)); + } +} diff --git a/src/main/java/com/tuinity/tuinity/chunk/light/SkyStarLightEngine.java b/src/main/java/com/tuinity/tuinity/chunk/light/SkyStarLightEngine.java new file mode 100644 -index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998d54596a5 +index 0000000000000000000000000000000000000000..41d9234a6b265f5500269cef537ad0b68323b025 --- /dev/null +++ b/src/main/java/com/tuinity/tuinity/chunk/light/SkyStarLightEngine.java -@@ -0,0 +1,365 @@ +@@ -0,0 +1,785 @@ +package com.tuinity.tuinity.chunk.light; + ++import it.unimi.dsi.fastutil.shorts.ShortCollection; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.ChunkCoordIntPair; +import net.minecraft.server.ChunkSection; @@ -1876,8 +2022,341 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + +public final class SkyStarLightEngine extends StarLightEngine { + -+ public SkyStarLightEngine() { -+ super(true); ++ /* ++ Specification for managing the initialisation and de-initialisation of skylight nibble arrays: ++ ++ Skylight nibble initialisation requires that non-empty chunk sections have 1 radius nibbles non-null. ++ ++ This presents some problems, as vanilla is only guaranteed to have 0 radius neighbours loaded when editing blocks. ++ However starlight fixes this so that it has 1 radius loaded. Still, we don't actually have guarantees ++ that we have the necessary chunks loaded to de-initialise neighbour sections (but we do have enough to de-initialise ++ our own) - we need a radius of 2 to de-initialise neighbour nibbles. ++ How do we solve this? ++ ++ Each chunk will store the last known "emptiness" of sections for each of their 1 radius neighbour chunk sections. ++ If the chunk does not have full data, then its nibbles are NOT de-initialised. This is because obviously the ++ chunk did not go through the light stage yet - or its neighbours are not lit. In either case, once the last ++ known "emptiness" of neighbouring sections is filled with data, the chunk will run a full check of the data ++ to see if any of its nibbles need to be de-initialised. ++ ++ The emptiness map allows us to de-initialise neighbour nibbles if the neighbour has it filled with data, ++ and if it doesn't have data then we know it will correctly de-initialise once it fills up. ++ ++ Unlike vanilla, we store whether nibbles are uninitialised on disk - so we don't need any dumb hacking ++ around those. ++ */ ++ ++ @Override ++ protected void handleEmptySectionChanges(final ILightAccess lightAccess, final IChunkAccess chunk, ++ final Boolean[] emptinessChanges, final boolean unlit) { ++ final int chunkX = chunk.getPos().x; ++ final int chunkZ = chunk.getPos().z; ++ ++ final boolean[][] chunkEmptinessMap = this.getEmptinessMap(chunkX, chunkZ); ++ ++ // index = (cx + 2) + 5*(cz + 2) ++ long loadedNeighboursBitset = 0L; ++ long unloadedNeighbourBitset = 0L; ++ ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ final IChunkAccess neighbour = this.getChunkInCache(dx + chunkX, dz + chunkZ); ++ if (neighbour == null) { ++ continue; ++ } ++ ++ final boolean[][] neighbourEmptinessMap = this.getEmptinessMap(dx + chunkX, dz + chunkZ); ++ for (int i = 0; i < neighbourEmptinessMap.length; ++i) { ++ // index = (cx + 1) + 3*(cz + 1) ++ final int dx2 = (i % 3) - 1; ++ final int dz2 = (i / 3) - 1; ++ ++ final int bitsetIndex = (dx2 + dx + 2) + 5*(dz2 + dz + 2); ++ if (neighbourEmptinessMap[i] == null) { ++ unloadedNeighbourBitset |= 1L << bitsetIndex; ++ } else { ++ loadedNeighboursBitset |= 1L << bitsetIndex; ++ } ++ } ++ } ++ } ++ ++ loadedNeighboursBitset &= ~unloadedNeighbourBitset; ++ loadedNeighboursBitset |= 1L << ((0 + 2) + 5*(0 + 2)); ++ ++ final boolean[] needsDeInitCheck = new boolean[9]; ++ final boolean needsInit = unlit || chunkEmptinessMap[IChunkAccess.getEmptinessMapIndex(0, 0)] == null; ++ if (needsInit) { ++ chunkEmptinessMap[IChunkAccess.getEmptinessMapIndex(0, 0)] = new boolean[16]; ++ } ++ ++ // this chunk is new, so we need to init neighbours ++ // because this chunk might have been modified inbetween loading/saving, we have to rewrite the emptiness map ++ // for our neighbours, so don't bother checking if they exist & whether they even needed a de-init recalc ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ final IChunkAccess neighbour = this.getChunkInCache(dx + chunkX, dz + chunkZ); ++ if (neighbour == null) { ++ // if the neighbour hasn't initialised its own empty map, we can't use it ++ // when it does though, it'll come by and initialise our map for it ++ continue; ++ } ++ final boolean[][] neighbourEmptinessMap = this.getEmptinessMap(dx + chunkX, dz + chunkZ); ++ ++ if (needsInit && (dx | dz) != 0) { ++ // init neighbour ++ neighbourEmptinessMap[IChunkAccess.getEmptinessMapIndex(-dx, -dz)] = new boolean[16]; ++ ++ if (neighbourEmptinessMap[IChunkAccess.getEmptinessMapIndex(0, 0)] != null) { ++ // init ourselves ++ System.arraycopy( ++ neighbourEmptinessMap[IChunkAccess.getEmptinessMapIndex(0, 0)], ++ 0, ++ chunkEmptinessMap[IChunkAccess.getEmptinessMapIndex(dx, dz)] = new boolean[16], ++ 0, ++ 16 ++ ); ++ } ++ } ++ ++ // check if our neighbours are ready for a recalc ++ ++ long neighboursMask = 0L; ++ for (int dz2 = -1; dz2 <= 1; ++dz2) { ++ for (int dx2 = -1; dx2 <= 1; ++dx2) { ++ neighboursMask |= 1L << ((dx2 + dx + 2) + 5*(dz2 + dz + 2)); ++ } ++ } ++ ++ if ((loadedNeighboursBitset & neighboursMask) == neighboursMask) { ++ // can check for de-init ++ needsDeInitCheck[(dx + 1) + 3 * (dz + 1)] = true; ++ } ++ } ++ } ++ ++ for (int sectionY = (emptinessChanges.length - 1); sectionY >= 0; --sectionY) { ++ final Boolean valueBoxed = emptinessChanges[sectionY]; ++ if (valueBoxed == null) { ++ continue; ++ } ++ ++ final boolean empty = valueBoxed.booleanValue(); ++ ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ final IChunkAccess neighbour = this.getChunkInCache(dx + chunkX, dz + chunkZ); ++ if (neighbour == null) { ++ // this is the case on the client, we _assume_ the server inits and sends to us. ++ // or we're on the server, and the neighbours haven't generated light yet. ++ continue; ++ } ++ ++ // init nibbles as needed ++ ++ if (!empty) { ++ // if we're not empty, we also need to initialise nibbles ++ // note: if we're unlit, we absolutely do not want to extrude, as light data isn't set up ++ final boolean extrude = (dx | dz) != 0 || !unlit; ++ for (int dy = 1; dy >= -1; --dy) { ++ this.initNibbleForLitChunk(dx + chunkX, dy + sectionY, dz + chunkZ, extrude, false); ++ } ++ } ++ ++ // update neighbour map ++ this.getEmptinessMap(dx + chunkX, dz + chunkZ)[IChunkAccess.getEmptinessMapIndex(-dx, -dz)][sectionY] = empty; ++ } ++ } ++ } ++ ++ // check for de-init, only runs if this just had data loaded in (or is being lit) ++ for (int i = 0; i < needsDeInitCheck.length; ++i) { ++ if (!needsDeInitCheck[i]) { ++ continue; ++ } ++ ++ // index = (cx + 1) + 3*(cz + 1) ++ final int neighbourX = (i % 3) - 1 + chunkX; ++ final int neighbourZ = (i / 3) - 1 + chunkZ; ++ ++ final boolean[][] neighbourEmptinessMap = this.getEmptinessMap(neighbourX, neighbourZ); ++ ++ for (int sectionY = 16; sectionY >= -1; --sectionY) { ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(neighbourX, sectionY, neighbourZ); ++ if (nibble == null || nibble.isNullNibbleUpdating()) { ++ // already null ++ continue; ++ } ++ ++ // check neighbours to see if we need to de-init this one ++ boolean allEmpty = true; ++ neighbour_search: ++ for (int dy = -1; dy <= 1; ++dy) { ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ final int y = sectionY + dy; ++ if (y < 0 || y > 15) { ++ // empty ++ continue; ++ } ++ if (!neighbourEmptinessMap[IChunkAccess.getEmptinessMapIndex(dx, dz)][y]) { ++ allEmpty = false; ++ break neighbour_search; ++ } ++ } ++ } ++ } ++ ++ if (allEmpty) { ++ // all were empty, so de-init ++ nibble.setNull(); ++ } ++ } ++ } ++ } ++ ++ protected final void initNibbleForLitChunk(final int chunkX, final int chunkY, final int chunkZ, final boolean extrude, ++ final boolean initRemovedNibbles) { ++ if (chunkY < -1 || chunkY > 16 || this.getChunkInCache(chunkX, chunkZ) == null) { ++ return; ++ } ++ SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ if (nibble == null) { ++ if (!initRemovedNibbles) { ++ throw new IllegalStateException(); ++ } else { ++ this.setNibbleInCache(chunkX, chunkY, chunkZ, nibble = new SWMRNibbleArray(null, true)); ++ } ++ } ++ this.initNibbleForLitChunk(nibble, chunkX, chunkY, chunkZ, extrude); ++ } ++ ++ protected final void initNibbleForLitChunk(final SWMRNibbleArray currNibble, final int chunkX, final int chunkY, final int chunkZ, final boolean extrude) { ++ if (!currNibble.isNullNibbleUpdating()) { ++ // already initialised ++ return; ++ } ++ ++ final boolean[] emptinessMap = this.getEmptinessMap(chunkX, chunkZ)[IChunkAccess.getEmptinessMapIndex(0, 0)]; ++ ++ // are we above this chunk's lowest empty section? ++ int lowestY = -2; ++ for (int currY = 15; currY >= 0; --currY) { ++ if (emptinessMap == null) { ++ // cannot delay nibble init for lit chunks, as we need to init to propagate into them. ++ final ChunkSection current = this.getChunkSection(chunkX, currY, chunkZ); ++ if (current == null || current == EMPTY_CHUNK_SECTION) { ++ continue; ++ } ++ } else { ++ if (emptinessMap[currY]) { ++ continue; ++ } ++ } ++ ++ // should always be full lit here ++ lowestY = currY; ++ break; ++ } ++ ++ if (chunkY > lowestY) { ++ // we need to set this one to full ++ this.getNibbleFromCache(chunkX, chunkY, chunkZ).setFull(); ++ return; ++ } ++ ++ if (extrude) { ++ // this nibble is going to depend solely on the skylight data above it ++ // find first non-null data above (there does exist one, as we just found it above) ++ for (int currY = chunkY + 1; currY <= 16; ++currY) { ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, currY, chunkZ); ++ if (nibble != null && !nibble.isNullNibbleUpdating()) { ++ currNibble.extrudeLower(nibble); ++ break; ++ } ++ } ++ } else { ++ currNibble.setNonNull(); ++ } ++ } ++ ++ protected final void rewriteNibbleCacheForSkylight(final IChunkAccess chunk) { ++ for (int index = 0, max = this.nibbleCache.length; index < max; ++index) { ++ final SWMRNibbleArray nibble = this.nibbleCache[index]; ++ if (nibble != null && nibble.isNullNibbleUpdating()) { ++ // stop propagation in these areas ++ this.nibbleCache[index] = null; ++ nibble.updateVisible(); ++ } ++ } ++ } ++ ++ protected final boolean[] nullPropagationCheckCache = new boolean[16 - (-1) + 1]; ++ ++ // rets whether neighbours were init'd ++ ++ protected final boolean checkNullSection(final int chunkX, final int chunkY, final int chunkZ, ++ final boolean extrudeInitialised) { ++ // null chunk sections may have nibble neighbours in the horizontal 1 radius that are ++ // non-null. Propagation to these neighbours is necessary. ++ // What makes this easy is we know none of these neighbours are non-empty (otherwise ++ // this nibble would be initialised). So, we don't have to initialise ++ // the neighbours in the full 1 radius, because there's no worry that any "paths" ++ // to the neighbours on this horizontal plane are blocked. ++ if (chunkY < -1 || chunkY > 16 || this.nullPropagationCheckCache[chunkY + 1]) { ++ return false; ++ } ++ this.nullPropagationCheckCache[chunkY + 1] = true; ++ ++ // check horizontal neighbours ++ boolean needInitNeighbours = false; ++ neighbour_search: ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(dx + chunkX, chunkY, dz + chunkZ); ++ if (nibble != null && !nibble.isNullNibbleUpdating()) { ++ needInitNeighbours = true; ++ break neighbour_search; ++ } ++ } ++ } ++ ++ if (needInitNeighbours) { ++ for (int dz = -1; dz <= 1; ++dz) { ++ for (int dx = -1; dx <= 1; ++dx) { ++ this.initNibbleForLitChunk(dx + chunkX, chunkY, dz + chunkZ, (dx | dz) == 0 ? extrudeInitialised : true, true); ++ } ++ } ++ } ++ ++ return needInitNeighbours; ++ } ++ ++ protected final int getLightLevelExtruded(final int worldX, final int worldY, final int worldZ) { ++ final int chunkX = worldX >> 4; ++ int chunkY = worldY >> 4; ++ final int chunkZ = worldZ >> 4; ++ ++ SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ if (nibble != null) { ++ return nibble.getUpdating(worldX, worldY, worldZ); ++ } ++ ++ for (;;) { ++ if (++chunkY > 16) { ++ return 15; ++ } ++ ++ nibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ ++ if (nibble != null) { ++ return nibble.getUpdating(worldX, 0, worldZ); ++ } ++ } ++ } ++ ++ public SkyStarLightEngine(final boolean isClientSide) { ++ super(true, isClientSide); + } + + @Override @@ -1886,17 +2365,37 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + } + + @Override ++ protected boolean[][] getEmptinessMap(final IChunkAccess chunk) { ++ return chunk.getEmptinessMap(); ++ } ++ ++ @Override + protected void setNibbles(final IChunkAccess chunk, final SWMRNibbleArray[] to) { + chunk.setSkyNibbles(to); + } + + @Override + protected boolean canUseChunk(final IChunkAccess chunk) { -+ return chunk.getChunkStatus().isAtLeastStatus(ChunkStatus.LIGHT) && chunk.isLit(); ++ // can only use chunks for sky stuff if their sections have been init'd ++ return chunk.getChunkStatus().isAtLeastStatus(ChunkStatus.LIGHT) ++ && (this.isClientSide ? chunk.getEmptinessMap()[IChunkAccess.getEmptinessMapIndex(0, 0)] != null : chunk.isLit()); + } + + @Override -+ protected final void checkBlock(final int worldX, final int worldY, final int worldZ) { ++ protected void checkChunkEdges(final ILightAccess lightAccess, final IChunkAccess chunk, final int fromSection, ++ final int toSection) { ++ this.rewriteNibbleCacheForSkylight(chunk); ++ super.checkChunkEdges(lightAccess, chunk, fromSection, toSection); ++ } ++ ++ @Override ++ protected void checkChunkEdges(final ILightAccess lightAccess, final IChunkAccess chunk, final ShortCollection sections) { ++ this.rewriteNibbleCacheForSkylight(chunk); ++ super.checkChunkEdges(lightAccess, chunk, sections); ++ } ++ ++ @Override ++ protected void checkBlock(final int worldX, final int worldY, final int worldZ) { + // blocks can change opacity + // blocks can change direction of propagation + @@ -1908,27 +2407,31 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + + if (currentLevel == 15) { + // must re-propagate clobbered source -+ this.increaseQueue[this.increaseQueueInitialLength++] = (worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) | -+ currentLevel << (6 + 6 + 9) | -+ ((Direction.POSITIVE_X.ordinal() | 8) << (6 + 6 + 9 + 4)) | -+ (FLAG_HAS_SIDED_TRANSPARENT_BLOCKS); // don't know if the block is conditionally transparent ++ this.increaseQueue[this.increaseQueueInitialLength++] = ++ ((worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (currentLevel & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; // don't know if the block is conditionally transparent + } else { + this.setLightLevel(worldX, worldY, worldZ, 0); + } + -+ this.decreaseQueue[this.decreaseQueueInitialLength++] = (worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) | -+ (currentLevel) << (6 + 6 + 9) | -+ ((Direction.POSITIVE_X.ordinal() | 8) << (6 + 6 + 9 + 4)); ++ this.decreaseQueue[this.decreaseQueueInitialLength++] = ++ ((worldX + (worldZ << 6) + (worldY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (currentLevel & 0xFL) << (6 + 6 + 16) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)); + } + -+ protected final int[] heightMap = new int[16 * 16]; ++ protected final int[] heightMapBlockChange = new int[16 * 16]; + { -+ Arrays.fill(this.heightMap, -1024); // clear heightmap ++ Arrays.fill(this.heightMapBlockChange, -1024); // clear heightmap + } + + @Override -+ protected void propagateBlockChanges(final ILightAccess lightAccess, final IChunkAccess atChunk, -+ final Set positions) { ++ protected void propagateBlockChanges(final ILightAccess lightAccess, final IChunkAccess atChunk, final Set positions) { ++ this.rewriteNibbleCacheForSkylight(atChunk); ++ Arrays.fill(this.nullPropagationCheckCache, false); ++ + final IBlockAccess world = lightAccess.getWorld(); + final int chunkX = atChunk.getPos().x; + final int chunkZ = atChunk.getPos().z; @@ -1938,52 +2441,79 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + int highestBlockY = -1024; + for (final BlockPosition pos : positions) { + final int index = pos.getX() + (pos.getZ() << 4) + heightMapOffset; -+ final int curr = this.heightMap[index]; ++ final int curr = this.heightMapBlockChange[index]; + if (pos.getY() > curr) { -+ this.heightMap[index] = pos.getY(); ++ this.heightMapBlockChange[index] = pos.getY(); + } + if (pos.getY() > highestBlockY) { + highestBlockY = pos.getY(); + } + } + ++ // note: light sets are delayed while processing skylight source changes due to how ++ // nibbles are initialised, as we want to avoid clobbering nibble values so what when ++ // below nibbles are initialised they aren't reading from partially modified nibbles ++ + // now we can recalculate the sources for the changed columns + for (int index = 0; index < (16 * 16); ++index) { -+ final int maxY = this.heightMap[index]; ++ final int maxY = this.heightMapBlockChange[index]; + if (maxY == -1024) { + // not changed + continue; + } -+ this.heightMap[index] = -1024; // restore default for next caller ++ this.heightMapBlockChange[index] = -1024; // restore default for next caller + + final int columnX = (index & 15) | (chunkX << 4); + final int columnZ = (index >>> 4) | (chunkZ << 4); + + // try and propagate from the above y -+ int maxPropagationY = this.tryPropagateSkylight(world, columnX, maxY, columnZ); ++ // delay light set until after processing all sources to setup ++ final int maxPropagationY = this.tryPropagateSkylight(world, columnX, maxY, columnZ, true, true); + + // maxPropagationY is now the highest block that could not be propagated to + -+ // remove all sources below that are not 15 -+ final int propagateDirection = Direction.NEGATIVE_Y.ordinal(); ++ // remove all sources below that are 15 ++ final long propagateDirection = AxisDirection.POSITIVE_Y.everythingButThisDirection; + final int encodeOffset = this.coordinateOffset; -+ for (int currY = maxPropagationY; currY >= -15; --currY) { -+ if (this.getLightLevel(columnX, currY, columnZ) != 15) { -+ break; ++ ++ if (this.getLightLevelExtruded(columnX, maxPropagationY, columnZ) == 15) { ++ // ensure section is checked ++ this.checkNullSection(columnX >> 4, maxPropagationY >> 4, columnZ >> 4, true); ++ ++ for (int currY = maxPropagationY; currY >= (-1 << 4); --currY) { ++ if ((currY & 15) == 15) { ++ // ensure section is checked ++ this.checkNullSection(columnX >> 4, (currY >> 4), columnZ >> 4, true); ++ } ++ ++ // ensure section below is always checked ++ final SWMRNibbleArray nibble = this.getNibbleFromCache(columnX >> 4, currY >> 4, columnZ >> 4); ++ if (nibble == null) { ++ // advance currY to the the top of the section below ++ currY = (currY) & (~15); ++ // note: this value ^ is actually 1 above the top, but the loop decrements by 1 so we actually ++ // end up there ++ continue; ++ } ++ ++ if (nibble.getUpdating(columnX, currY, columnZ) != 15) { ++ break; ++ } ++ ++ // delay light set until after processing all sources to setup ++ this.decreaseQueue[this.decreaseQueueInitialLength++] = ++ ((columnX + (columnZ << 6) + (currY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (15L << (6 + 6 + 16)) ++ | (propagateDirection << (6 + 6 + 16 + 4)); ++ // do not set transparent blocks for the same reason we don't in the checkBlock method + } -+ this.setLightLevel(columnX, currY, columnZ, 0); -+ this.decreaseQueue[this.decreaseQueueInitialLength++] = (columnX + (columnZ << 6) + (currY << (6 + 6)) + encodeOffset) | -+ (15 << (6 + 6 + 9)) | -+ ((propagateDirection) << (6 + 6 + 9 + 4)); -+ // do not set transparent blocks for the same reason we don't in the checkBlock method + } + } + -+ // we need to initialise nibbles up to the highest section (we don't save null nibbles) -+ for (int y = -1; y <= Math.min(16, (highestBlockY >> 4)); ++y) { -+ final SWMRNibbleArray nibble = this.getNibbleFromCache(chunkX, y, chunkZ); -+ nibble.markNonNull(); -+ } ++ // delayed light sets are processed here, and must be processed before checkBlock as checkBlock reads ++ // immediate light value ++ this.processDelayedIncreases(); ++ this.processDelayedDecreases(); + + for (final BlockPosition pos : positions) { + this.checkBlock(pos.getX(), pos.getY(), pos.getZ()); @@ -1992,65 +2522,32 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + this.performLightDecrease(lightAccess); + } + -+ protected void initLightNeighbours(final int chunkX, final int chunkZ) { -+ // vanilla requires that written nibble data has initialised nibble data in 1 radius -+ for (int dz = -1; dz <= 1; ++dz) { -+ for (int dx = -1; dx <= 1; ++dx) { -+ IChunkAccess chunk = this.getChunkInCache(dx + chunkX, dz + chunkZ); -+ if (chunk == null) { -+ continue; -+ } -+ // find lowest section -+ int lowest = 15; -+ ChunkSection[] sections = chunk.getSections(); -+ for (;lowest > 0 && (sections[lowest] == null || sections[lowest].isFullOfAir()); --lowest) {} -+ -+ if (lowest == -1) { -+ continue; -+ } -+ -+ for (int y = lowest; y >= -1; --y) { -+ SWMRNibbleArray nibble = this.getNibbleFromCache(dx + chunkX, y, dz + chunkZ); -+ if (nibble != null && !nibble.isDirty() && nibble.isInitialisedUpdating()) { -+ for (int dy = -1; dy <= 1; ++dy) { -+ SWMRNibbleArray ours = this.getNibbleFromCache(chunkX, dy + y, chunkZ); -+ if (ours != null && !ours.isDirty() && ours.isNullNibbleUpdating()) { -+ ours.initialiseWorking(); -+ ours.updateVisible(); -+ } -+ } -+ } -+ } -+ } -+ } -+ } -+ + @Override + protected void lightChunk(final ILightAccess lightAccess, final IChunkAccess chunk, final boolean needsEdgeChecks) { ++ this.rewriteNibbleCacheForSkylight(chunk); ++ Arrays.fill(this.nullPropagationCheckCache, false); ++ + final IBlockAccess world = lightAccess.getWorld(); + final ChunkCoordIntPair chunkPos = chunk.getPos(); + final int chunkX = chunkPos.x; + final int chunkZ = chunkPos.z; + + final ChunkSection[] sections = chunk.getSections(); -+ final SWMRNibbleArray[] originalNibbles = this.getNibblesForChunkFromCache(chunkX, chunkZ); + -+ int highestNonEmptySection = 16; -+ while (highestNonEmptySection == -1 || highestNonEmptySection == 16 || sections[highestNonEmptySection] == null || sections[highestNonEmptySection].isFullOfAir()) { ++ int highestNonEmptySection = 15; ++ while (highestNonEmptySection == -1 || ++ sections[highestNonEmptySection] == null || sections[highestNonEmptySection].isFullOfAir()) { ++ this.checkNullSection(chunkX, highestNonEmptySection, chunkZ, false); + // try propagate FULL to neighbours + + // check neighbours to see if we need to propagate into them -+ for (final Direction direction : ONLY_HORIZONTAL_DIRECTIONS) { ++ for (final AxisDirection direction : ONLY_HORIZONTAL_DIRECTIONS) { + final int neighbourX = chunkX + direction.x; + final int neighbourZ = chunkZ + direction.z; + final SWMRNibbleArray neighbourNibble = this.getNibbleFromCache(neighbourX, highestNonEmptySection, neighbourZ); + if (neighbourNibble == null) { + // unloaded neighbour -+ continue; -+ } -+ if (neighbourNibble.isNullNibbleUpdating()) { + // most of the time we fall here -+ // no point of propagating full light into full light + continue; + } + @@ -2088,14 +2585,15 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + } + + final int encodeOffset = this.coordinateOffset; -+ final int propagateDirection = direction.ordinal() | 16; // we only want to check in this direction ++ final long propagateDirection = 1L << direction.ordinal(); // we only want to check in this direction + + for (int currY = highestNonEmptySection << 4, maxY = currY | 15; currY <= maxY; ++currY) { + for (int i = 0, currX = startX, currZ = startZ; i < 16; ++i, currX += incX, currZ += incZ) { -+ this.increaseQueue[this.increaseQueueInitialLength++] = (currX + (currZ << 6) + (currY << (6 + 6)) + encodeOffset) | -+ (15 << (6 + 6 + 9)) | // we know we're at full lit here -+ ((propagateDirection) << (6 + 6 + 9 + 4)); -+ // no transparent flag, we know for a fact there are no blocks here that could be directionally transparent (as the section is EMPTY) ++ this.increaseQueue[this.increaseQueueInitialLength++] = ++ ((currX + (currZ << 6) + (currY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (15L << (6 + 6 + 16)) // we know we're at full lit here ++ | (propagateDirection << (6 + 6 + 16 + 4)); ++ // no transparent flag, we know for a fact there are no blocks here that could be directionally transparent (as the section is EMPTY) + } + } + } @@ -2106,11 +2604,6 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + } + + if (highestNonEmptySection >= 0) { -+ // mark the rest of our nibbles as 0 -+ for (int currY = highestNonEmptySection; currY >= -1; --currY) { -+ this.getNibbleFromCache(chunkX, currY, chunkZ).markNonNull(); -+ } -+ + // fill out our other sources + final int minX = chunkPos.x << 4; + final int maxX = chunkPos.x << 4 | 15; @@ -2119,14 +2612,15 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + final int startY = highestNonEmptySection << 4 | 15; + for (int currZ = minZ; currZ <= maxZ; ++currZ) { + for (int currX = minX; currX <= maxX; ++currX) { -+ final int end = this.tryPropagateSkylight(world, currX, startY, currZ); ++ final int end = this.tryPropagateSkylight(world, currX, startY, currZ, false, false); + if (end == startY) { + // we need to propagate this one ourselves. -+ this.increaseQueue[this.increaseQueueInitialLength++] = (currX + (currZ << 6) + (startY << (6 + 6)) + this.coordinateOffset) | -+ (15 << (6 + 6 + 9)) | // we know we're at full lit here -+ ((Direction.NEGATIVE_Y.ordinal()) << (6 + 6 + 9 + 4)); // no need to check upwards, we know it's 15. -+ // we know this block is air because the section is empty, so it's obviously not sidedly -+ // transparent. ++ this.increaseQueue[this.increaseQueueInitialLength++] = ++ ((currX + (currZ << 6) + (startY << (6 + 6)) + this.coordinateOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (15L << (6 + 6 + 16)) // we know we're at full lit here ++ | (AxisDirection.POSITIVE_Y.everythingButThisDirection << (6 + 6 + 16 + 4)); // no need to check upwards, we know it's 15. ++ // we know this block is air because the section is empty, so it's obviously not sidedly ++ // transparent. + } + } + } @@ -2136,41 +2630,92 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + // not required to propagate here, but this will reduce the hit of the edge checks + this.performLightIncrease(lightAccess); + -+ this.checkChunkEdges(lightAccess, chunk); ++ for (int y = 16; y >= -1; --y) { ++ this.checkNullSection(chunkX, y, chunkZ, false); ++ } ++ this.checkChunkEdges(lightAccess, chunk, -1, 16); + } else { ++ for (int y = highestNonEmptySection; y >= -1; --y) { ++ this.checkNullSection(chunkX, y, chunkZ, false); ++ } + this.propagateNeighbourLevels(lightAccess, chunk, -1, highestNonEmptySection); + + this.performLightIncrease(lightAccess); + } -+ -+ this.initLightNeighbours(chunkPos.x, chunkPos.z); + } + -+ protected final int tryPropagateSkylight(final IBlockAccess world, final int worldX, final int startY, final int worldZ) { ++ protected final void processDelayedIncreases() { ++ // copied from performLightIncrease ++ final long[] queue = this.increaseQueue; ++ final int decodeOffsetX = -this.encodeOffsetX; ++ final int decodeOffsetY = -this.encodeOffsetY; ++ final int decodeOffsetZ = -this.encodeOffsetZ; ++ ++ for (int i = 0, len = this.increaseQueueInitialLength; i < len; ++i) { ++ final long queueValue = queue[i]; ++ ++ final int posX = ((int)queueValue & 63) + decodeOffsetX; ++ final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; ++ final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; ++ final int propagatedLightLevel = (int)((queueValue >>> (6 + 6 + 16)) & 0xF); ++ ++ this.setLightLevel(posX, posY, posZ, propagatedLightLevel); ++ } ++ } ++ ++ protected final void processDelayedDecreases() { ++ // copied from performLightDecrease ++ final long[] queue = this.decreaseQueue; ++ final int decodeOffsetX = -this.encodeOffsetX; ++ final int decodeOffsetY = -this.encodeOffsetY; ++ final int decodeOffsetZ = -this.encodeOffsetZ; ++ ++ for (int i = 0, len = this.decreaseQueueInitialLength; i < len; ++i) { ++ final long queueValue = queue[i]; ++ ++ final int posX = ((int)queueValue & 63) + decodeOffsetX; ++ final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; ++ final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; ++ ++ this.setLightLevel(posX, posY, posZ, 0); ++ } ++ } ++ ++ // delaying the light set is useful for block changes since they need to worry about initialising nibblearrays ++ // while also queueing light at the same time (initialising nibblearrays might depend on nibbles above, so ++ // clobbering the light values will result in broken propagation) ++ protected final int tryPropagateSkylight(final IBlockAccess world, final int worldX, int startY, final int worldZ, ++ final boolean extrudeInitialised, final boolean delayLightSet) { + final BlockPosition.MutableBlockPosition mutablePos = this.mutablePos3; + final int encodeOffset = this.coordinateOffset; -+ final int propagateDirection = Direction.NEGATIVE_Y.ordinal(); // just don't check upwards. ++ final long propagateDirection = AxisDirection.POSITIVE_Y.everythingButThisDirection; // just don't check upwards. + -+ if (this.getLightLevel(worldX, startY + 1, worldZ) != 15) { ++ if (this.getLightLevelExtruded(worldX, startY + 1, worldZ) != 15) { + return startY; + } + -+ IBlockData above = this.getBlockData(worldX, startY + 1, worldZ); ++ // ensure this section is always checked ++ this.checkNullSection(worldX >> 4, startY >> 4, worldZ >> 4, extrudeInitialised); ++ ++ IBlockData above = this.getBlockState(worldX, startY + 1, worldZ); + if (above == null) { -+ above = AIR_BLOCK_DATA; ++ above = AIR_BLOCK_STATE; + } + -+ int maxPropagationY; -+ for (maxPropagationY = startY; maxPropagationY >= -15; --maxPropagationY) { -+ IBlockData current = this.getBlockData(worldX, maxPropagationY, worldZ); ++ for (;startY >= (-1 << 4); --startY) { ++ if ((startY & 15) == 15) { ++ // ensure this section is always checked ++ this.checkNullSection(worldX >> 4, startY >> 4, worldZ >> 4, extrudeInitialised); ++ } ++ IBlockData current = this.getBlockState(worldX, startY, worldZ); + if (current == null) { -+ current = AIR_BLOCK_DATA; ++ current = AIR_BLOCK_STATE; + } + + final VoxelShape fromShape; + if (above.isConditionallyFullOpaque()) { -+ this.mutablePos2.setValues(worldX, maxPropagationY + 1, worldZ); -+ fromShape = above.getCullingFace(world, this.mutablePos2, Direction.NEGATIVE_Y.nms); ++ this.mutablePos2.setValues(worldX, startY + 1, worldZ); ++ fromShape = above.getCullingFace(world, this.mutablePos2, AxisDirection.NEGATIVE_Y.nms); + if (VoxelShapes.combinationOccludes(VoxelShapes.getEmptyShape(), fromShape)) { + // above wont let us propagate + break; @@ -2187,16 +2732,17 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + break; + } + // most of the time it falls here. -+ this.setLightLevel(worldX, maxPropagationY, worldZ, 15); + // add to propagate -+ this.increaseQueue[this.increaseQueueInitialLength++] = (worldX + (worldZ << 6) + (maxPropagationY << (6 + 6)) + encodeOffset) | -+ (15 << (6 + 6 + 9)) | // we know we're at full lit here -+ ((propagateDirection) << (6 + 6 + 9 + 4)); ++ // light set delayed until we determine if this nibble section is null ++ this.increaseQueue[this.increaseQueueInitialLength++] = ++ ((worldX + (worldZ << 6) + (startY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (15L << (6 + 6 + 16)) // we know we're at full lit here ++ | (propagateDirection << (6 + 6 + 16 + 4)); + } else { -+ mutablePos.setValues(worldX, maxPropagationY, worldZ); -+ int flags = 0; ++ mutablePos.setValues(worldX, startY, worldZ); ++ long flags = 0L; + if (current.isConditionallyFullOpaque()) { -+ final VoxelShape cullingFace = current.getCullingFace(world, mutablePos, Direction.POSITIVE_Y.nms); ++ final VoxelShape cullingFace = current.getCullingFace(world, mutablePos, AxisDirection.POSITIVE_Y.nms); + + if (VoxelShapes.combinationOccludes(fromShape, cullingFace)) { + // can't propagate here, we're done on this column. @@ -2211,28 +2757,49 @@ index 0000000000000000000000000000000000000000..055f2e1a469830294767af8eb733e998 + break; + } + -+ this.setLightLevel(worldX, maxPropagationY, worldZ, 15); -+ this.increaseQueue[this.increaseQueueInitialLength++] = (worldX + (worldZ << 6) + (maxPropagationY << (6 + 6)) + encodeOffset) | -+ (15 << (6 + 6 + 9)) | // we know we're at full lit here -+ ((propagateDirection) << (6 + 6 + 9 + 4)) | -+ flags; ++ // light set delayed until we determine if this nibble section is null ++ this.increaseQueue[this.increaseQueueInitialLength++] = ++ ((worldX + (worldZ << 6) + (startY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | (15L << (6 + 6 + 16)) // we know we're at full lit here ++ | (propagateDirection << (6 + 6 + 16 + 4)) ++ | flags; + } + + above = current; ++ ++ if (this.getNibbleFromCache(worldX >> 4, startY >> 4, worldZ >> 4) == null) { ++ // we skip empty sections here, as this is just an easy way of making sure the above block ++ // can propagate through air. ++ ++ // nothing can propagate in null sections, remove the queue entry for it ++ --this.increaseQueueInitialLength; ++ ++ // advance currY to the the top of the section below ++ startY = (startY) & (~15); ++ // note: this value ^ is actually 1 above the top, but the loop decrements by 1 so we actually ++ // end up there ++ ++ // make sure this is marked as AIR ++ above = AIR_BLOCK_STATE; ++ } else if (!delayLightSet) { ++ this.setLightLevel(worldX, startY, worldZ, 15); ++ } + } + -+ return maxPropagationY; ++ return startY; + } +} diff --git a/src/main/java/com/tuinity/tuinity/chunk/light/StarLightEngine.java b/src/main/java/com/tuinity/tuinity/chunk/light/StarLightEngine.java new file mode 100644 -index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bda23e6dd1 +index 0000000000000000000000000000000000000000..38b84cee50a5e97ba12dcfba472c607064d4eec2 --- /dev/null +++ b/src/main/java/com/tuinity/tuinity/chunk/light/StarLightEngine.java -@@ -0,0 +1,1014 @@ +@@ -0,0 +1,1223 @@ +package com.tuinity.tuinity.chunk.light; + -+import com.destroystokyo.paper.util.math.IntegerUtil; ++import com.tuinity.tuinity.util.IntegerUtil; ++import it.unimi.dsi.fastutil.shorts.ShortCollection; ++import it.unimi.dsi.fastutil.shorts.ShortIterator; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.Blocks; +import net.minecraft.server.ChunkCoordIntPair; @@ -2253,18 +2820,18 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + +public abstract class StarLightEngine { + -+ protected static final IBlockData AIR_BLOCK_DATA = Blocks.AIR.getBlockData(); ++ protected static final IBlockData AIR_BLOCK_STATE = Blocks.AIR.getBlockData(); + + protected static final ChunkSection EMPTY_CHUNK_SECTION = new ChunkSection(0); + -+ protected static final Direction[] DIRECTIONS = Direction.values(); -+ protected static final Direction[] AXIS_DIRECTIONS = DIRECTIONS; -+ protected static final Direction[] ONLY_HORIZONTAL_DIRECTIONS = new Direction[] { -+ Direction.POSITIVE_X, Direction.NEGATIVE_X, -+ Direction.POSITIVE_Z, Direction.NEGATIVE_Z ++ protected static final AxisDirection[] DIRECTIONS = AxisDirection.values(); ++ protected static final AxisDirection[] AXIS_DIRECTIONS = DIRECTIONS; ++ protected static final AxisDirection[] ONLY_HORIZONTAL_DIRECTIONS = new AxisDirection[] { ++ AxisDirection.POSITIVE_X, AxisDirection.NEGATIVE_X, ++ AxisDirection.POSITIVE_Z, AxisDirection.NEGATIVE_Z + }; + -+ protected static enum Direction { ++ protected static enum AxisDirection { + + // Declaration order is important and relied upon. Do not change without modifying propagation code. + POSITIVE_X(1, 0, 0), NEGATIVE_X(-1, 0, 0), @@ -2277,21 +2844,26 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + POSITIVE_Y.opposite = NEGATIVE_Y; NEGATIVE_Y.opposite = POSITIVE_Y; + } + -+ protected Direction opposite; ++ protected AxisDirection opposite; + + public final int x; + public final int y; + public final int z; + public final EnumDirection nms; ++ public final long everythingButThisDirection; ++ public final long everythingButTheOppositeDirection; + -+ Direction(final int x, final int y, final int z) { ++ AxisDirection(final int x, final int y, final int z) { + this.x = x; + this.y = y; + this.z = z; + this.nms = EnumDirection.from(x, y, z); ++ this.everythingButThisDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << this.ordinal())); ++ // positive is always even, negative is always odd. Flip the 1 bit to get the negative direction. ++ this.everythingButTheOppositeDirection = (long)(ALL_DIRECTIONS_BITSET ^ (1 << (this.ordinal() ^ 1))); + } + -+ public Direction getOpposite() { ++ public AxisDirection getOpposite() { + return this.opposite; + } + } @@ -2313,10 +2885,18 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + // index = x + (z * 5) + (y * 25) + protected final SWMRNibbleArray[] nibbleCache = new SWMRNibbleArray[5 * 5 * (16 + 2 + 2)]; // add two extra sections for buffer + ++ // the exact same as above, except for storing fast access to nibbles to call change callbacks for ++ // for the y chunk section it's from [-1, 16] or [0, 17] ++ // index = x + (z * 5) + (y * 25) ++ protected final boolean[] notifyUpdateCache = new boolean[5 * 5 * (16 + 2 + 2)]; ++ + // always initialsed during start of lighting. no index is null. + // index = x + (z * 5) + protected final IChunkAccess[] chunkCache = new IChunkAccess[5 * 5]; + ++ // index = x + (z * 5) ++ protected final boolean[][][] emptinessMapCache = new boolean[5 * 5][][]; ++ + protected final BlockPosition.MutableBlockPosition mutablePos1 = new BlockPosition.MutableBlockPosition(); + protected final BlockPosition.MutableBlockPosition mutablePos2 = new BlockPosition.MutableBlockPosition(); + protected final BlockPosition.MutableBlockPosition mutablePos3 = new BlockPosition.MutableBlockPosition(); @@ -2336,10 +2916,12 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + + protected final boolean skylightPropagator; + protected final int emittedLightMask; ++ protected static final boolean isClientSide = false; + -+ protected StarLightEngine(final boolean skylightPropagator) { ++ protected StarLightEngine(final boolean skylightPropagator, final boolean isClientSide) { + this.skylightPropagator = skylightPropagator; + this.emittedLightMask = skylightPropagator ? 0 : 0xF; ++ //this.isClientSide = isClientSide; + } + + protected final void setupEncodeOffset(final int centerX, final int centerY, final int centerZ) { @@ -2363,7 +2945,7 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + this.chunkSectionIndexOffset = this.chunkIndexOffset + ((5 * 5) * this.chunkOffsetY); + } + -+ protected final void setupCaches(final ILightAccess world, final int centerX, final int centerY, final int centerZ, final boolean relaxed) { ++ protected final void setupCaches(final ILightAccess chunkProvider, final int centerX, final int centerY, final int centerZ, final boolean relaxed) { + final int centerChunkX = centerX >> 4; + final int centerChunkY = centerY >> 4; + final int centerChunkZ = centerZ >> 4; @@ -2377,7 +2959,7 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + + for (int cx = minX; cx <= maxX; ++cx) { + for (int cz = minZ; cz <= maxZ; ++cz) { -+ final IChunkAccess chunk = (IChunkAccess)world.getFeaturesReadyChunk(cx, cz); ++ final IChunkAccess chunk = (IChunkAccess)chunkProvider.getFeaturesReadyChunk(cx, cz); // mappings are awful here, this is the "get chunk at if at least features" + + if (chunk == null) { + if (relaxed) { @@ -2393,6 +2975,7 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + this.setChunkInCache(cx, cz, chunk); + this.setBlocksForChunkInCache(cx, cz, chunk.getSections()); + this.setNibblesForChunkInCache(cx, cz, this.getNibblesOnChunk(chunk)); ++ this.setEmptinessMapCache(cx, cz, this.getEmptinessMap(chunk)); + } + } + } @@ -2405,10 +2988,18 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + this.chunkCache[chunkX + 5*chunkZ + this.chunkIndexOffset] = chunk; + } + ++ protected final ChunkSection getChunkSection(final int chunkX, final int chunkY, final int chunkZ) { ++ return this.sectionCache[chunkX + 5*chunkZ + (5 * 5) * chunkY + this.chunkSectionIndexOffset]; ++ } ++ ++ protected final void setChunkSectionInCache(final int chunkX, final int chunkY, final int chunkZ, final ChunkSection section) { ++ this.sectionCache[chunkX + 5*chunkZ + 5*5*chunkY + this.chunkSectionIndexOffset] = section; ++ } ++ + protected final void setBlocksForChunkInCache(final int chunkX, final int chunkZ, final ChunkSection[] sections) { -+ final int chunkIndex = chunkX + 5*chunkZ; + for (int cy = -1; cy <= 16; ++cy) { -+ this.sectionCache[chunkIndex + (cy * (5 * 5)) + this.chunkSectionIndexOffset] = sections == null ? null : (cy >= 0 && cy <= 15 ? (sections[cy] == null || sections[cy].isFullOfAir() ? EMPTY_CHUNK_SECTION : sections[cy]) : EMPTY_CHUNK_SECTION); ++ this.setChunkSectionInCache(chunkX, cy, chunkZ, ++ sections == null ? null : (cy >= 0 && cy <= 15 ? (sections[cy] == null || sections[cy].isFullOfAir() ? EMPTY_CHUNK_SECTION : sections[cy]) : EMPTY_CHUNK_SECTION)); + } + } + @@ -2426,40 +3017,28 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + return ret; + } + ++ protected final void setNibbleInCache(final int chunkX, final int chunkY, final int chunkZ, final SWMRNibbleArray nibble) { ++ this.nibbleCache[chunkX + 5*chunkZ + (5 * 5) * chunkY + this.chunkSectionIndexOffset] = nibble; ++ } ++ + protected final void setNibblesForChunkInCache(final int chunkX, final int chunkZ, final SWMRNibbleArray[] nibbles) { + for (int cy = -1; cy <= 16; ++cy) { + this.setNibbleInCache(chunkX, cy, chunkZ, nibbles == null ? null : nibbles[cy + 1]); + } + } + -+ protected final void setNibbleInCache(final int chunkX, final int chunkY, final int chunkZ, final SWMRNibbleArray nibble) { -+ this.nibbleCache[chunkX + 5*chunkZ + (5 * 5) * chunkY + this.chunkSectionIndexOffset] = nibble; -+ } -+ + protected final void updateVisible(final ILightAccess lightAccess) { + for (int index = 0, max = this.nibbleCache.length; index < max; ++index) { + final SWMRNibbleArray nibble = this.nibbleCache[index]; -+ if (nibble != null && nibble.updateVisible()) { -+ final int chunkX = (index % 5) - this.chunkOffsetX; -+ final int chunkZ = ((index / 5) % 5) - this.chunkOffsetZ; -+ final int chunkY = ((index / (5*5)) % (16 + 2 + 2)) - this.chunkOffsetY; ++ if (!this.notifyUpdateCache[index] && (nibble == null || !nibble.isDirty())) { ++ continue; ++ } ++ ++ final int chunkX = (index % 5) - this.chunkOffsetX; ++ final int chunkZ = ((index / 5) % 5) - this.chunkOffsetZ; ++ final int chunkY = ((index / (5*5)) % (16 + 2 + 2)) - this.chunkOffsetY; ++ if ((nibble != null && nibble.updateVisible()) || this.notifyUpdateCache[index]) { + lightAccess.markLightSectionDirty(this.skylightPropagator ? EnumSkyBlock.SKY : EnumSkyBlock.BLOCK, new SectionPosition(chunkX, chunkY, chunkZ)); -+ // initialise 1 radius neighbours -+ if (this.skylightPropagator) { -+ for (int dy = -1; dy <= 1; ++dy) { -+ for (int dz = -1; dz <= 1; ++dz) { -+ for (int dx = -1; dx <= 1; ++dx) { -+ SWMRNibbleArray neighbour = this.getNibbleFromCache(chunkX + dx, chunkY + dy, chunkZ + dz); -+ if (neighbour != null && !neighbour.isDirty() && neighbour.isNullNibbleUpdating()) { -+ neighbour.initialiseWorking(); -+ neighbour.updateVisible(); -+ lightAccess.markLightSectionDirty(this.skylightPropagator ? EnumSkyBlock.SKY : EnumSkyBlock.BLOCK, -+ new SectionPosition(chunkX + dx, chunkY + dy, chunkZ + dz)); -+ } -+ } -+ } -+ } -+ } + } + } + } @@ -2468,23 +3047,27 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + Arrays.fill(this.sectionCache, null); + Arrays.fill(this.nibbleCache, null); + Arrays.fill(this.chunkCache, null); ++ Arrays.fill(this.emptinessMapCache, null); ++ if (this.isClientSide) { ++ Arrays.fill(this.notifyUpdateCache, false); ++ } + } + -+ protected final IBlockData getBlockData(final int worldX, final int worldY, final int worldZ) { ++ protected final IBlockData getBlockState(final int worldX, final int worldY, final int worldZ) { + final ChunkSection section = this.sectionCache[(worldX >> 4) + 5 * (worldZ >> 4) + (5 * 5) * (worldY >> 4) + this.chunkSectionIndexOffset]; + + if (section != null) { -+ return section == EMPTY_CHUNK_SECTION ? AIR_BLOCK_DATA : section.blockIds.rawGet((worldX & 15) | ((worldZ & 15) << 4) | ((worldY & 15) << 8)); ++ return section == EMPTY_CHUNK_SECTION ? AIR_BLOCK_STATE : section.getType(worldX & 15, worldY & 15, worldZ & 15); + } + + return null; + } + -+ protected final IBlockData getBlockData(final int sectionIndex, final int localIndex) { ++ protected final IBlockData getBlockState(final int sectionIndex, final int localIndex) { + final ChunkSection section = this.sectionCache[sectionIndex]; + + if (section != null) { -+ return section == EMPTY_CHUNK_SECTION ? AIR_BLOCK_DATA : section.blockIds.rawGet(localIndex); ++ return section == EMPTY_CHUNK_SECTION ? AIR_BLOCK_STATE : section.blockIds.rawGet(localIndex); + } + + return null; @@ -2503,35 +3086,94 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + } + + protected final void setLightLevel(final int worldX, final int worldY, final int worldZ, final int level) { -+ final SWMRNibbleArray nibble = this.nibbleCache[(worldX >> 4) + 5 * (worldZ >> 4) + (5 * 5) * (worldY >> 4) + this.chunkSectionIndexOffset]; ++ final int sectionIndex = (worldX >> 4) + 5 * (worldZ >> 4) + (5 * 5) * (worldY >> 4) + this.chunkSectionIndexOffset; ++ final SWMRNibbleArray nibble = this.nibbleCache[sectionIndex]; + + if (nibble != null) { + nibble.set((worldX & 15) | ((worldZ & 15) << 4) | ((worldY & 15) << 8), level); ++ if (this.isClientSide) { ++ int cx1 = (worldX - 1) >> 4; ++ int cx2 = (worldX + 1) >> 4; ++ int cy1 = (worldY - 1) >> 4; ++ int cy2 = (worldY + 1) >> 4; ++ int cz1 = (worldZ - 1) >> 4; ++ int cz2 = (worldZ + 1) >> 4; ++ for (int x = cx1; x <= cx2; ++x) { ++ for (int y = cy1; y <= cy2; ++y) { ++ for (int z = cz1; z <= cz2; ++z) { ++ this.notifyUpdateCache[x + 5 * z + (5 * 5) * y + this.chunkSectionIndexOffset] = true; ++ } ++ } ++ } ++ } + } + } + -+ protected final void setLightLevel(final int sectionIndex, final int localIndex, final int level) { ++ protected final void postLightUpdate(final int worldX, final int worldY, final int worldZ) { ++ if (this.isClientSide) { ++ int cx1 = (worldX - 1) >> 4; ++ int cx2 = (worldX + 1) >> 4; ++ int cy1 = (worldY - 1) >> 4; ++ int cy2 = (worldY + 1) >> 4; ++ int cz1 = (worldZ - 1) >> 4; ++ int cz2 = (worldZ + 1) >> 4; ++ for (int x = cx1; x <= cx2; ++x) { ++ for (int y = cy1; y <= cy2; ++y) { ++ for (int z = cz1; z <= cz2; ++z) { ++ this.notifyUpdateCache[x + (5 * z) + (5 * 5 * y) + this.chunkSectionIndexOffset] = true; ++ } ++ } ++ } ++ } ++ } ++ ++ protected final void setLightLevel(final int sectionIndex, final int localIndex, final int worldX, final int worldY, final int worldZ, final int level) { + final SWMRNibbleArray nibble = this.nibbleCache[sectionIndex]; + + if (nibble != null) { + nibble.set(localIndex, level); ++ if (this.isClientSide) { ++ int cx1 = (worldX - 1) >> 4; ++ int cx2 = (worldX + 1) >> 4; ++ int cy1 = (worldY - 1) >> 4; ++ int cy2 = (worldY + 1) >> 4; ++ int cz1 = (worldZ - 1) >> 4; ++ int cz2 = (worldZ + 1) >> 4; ++ for (int x = cx1; x <= cx2; ++x) { ++ for (int y = cy1; y <= cy2; ++y) { ++ for (int z = cz1; z <= cz2; ++z) { ++ this.notifyUpdateCache[x + (5 * z) + (5 * 5 * y) + this.chunkSectionIndexOffset] = true; ++ } ++ } ++ } ++ } + } + } + -+ public static SWMRNibbleArray[] getFilledEmptyLight(final boolean skylight) { ++ protected final boolean[][] getEmptinessMap(final int chunkX, final int chunkZ) { ++ return this.emptinessMapCache[chunkX + 5*chunkZ + this.chunkIndexOffset]; ++ } ++ ++ protected final void setEmptinessMapCache(final int chunkX, final int chunkZ, final boolean[][] emptinessMap) { ++ this.emptinessMapCache[chunkX + 5*chunkZ + this.chunkIndexOffset] = emptinessMap; ++ } ++ ++ public static SWMRNibbleArray[] getFilledEmptyLight() { + final SWMRNibbleArray[] ret = getEmptyLightArray(); + + for (int i = 0, len = ret.length; i < len; ++i) { -+ ret[i] = new SWMRNibbleArray(true, skylight ? 15 : 0); ++ ret[i] = new SWMRNibbleArray(null, true); + } + + return ret; + } + + public static SWMRNibbleArray[] getEmptyLightArray() { -+ return new SWMRNibbleArray[16 + 2]; ++ return new SWMRNibbleArray[16 - (-1) + 1]; + } + ++ protected abstract boolean[][] getEmptinessMap(final IChunkAccess chunk); ++ + protected abstract SWMRNibbleArray[] getNibblesOnChunk(final IChunkAccess chunk); + + protected abstract void setNibbles(final IChunkAccess chunk, final SWMRNibbleArray[] to); @@ -2539,10 +3181,19 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + protected abstract boolean canUseChunk(final IChunkAccess chunk); + + public final void blocksChangedInChunk(final ILightAccess lightAccess, final int chunkX, final int chunkZ, -+ final Set positions) { -+ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, false); ++ final Set positions, final Boolean[] changedSections) { ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, this.isClientSide); + try { -+ this.propagateBlockChanges(lightAccess, this.getChunkInCache(chunkX, chunkZ), positions); ++ final IChunkAccess chunk = this.getChunkInCache(chunkX, chunkZ); ++ if (this.isClientSide && chunk == null) { ++ return; ++ } ++ if (changedSections != null) { ++ this.handleEmptySectionChanges(lightAccess, chunk, changedSections, false); ++ } ++ if (!positions.isEmpty()) { ++ this.propagateBlockChanges(lightAccess, chunk, positions); ++ } + this.updateVisible(lightAccess); + } finally { + this.destroyCaches(); @@ -2551,115 +3202,134 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + + // subclasses should not initialise caches, as this will always be done by the super call + // subclasses should not invoke updateVisible, as this will always be done by the super call -+ protected abstract void propagateBlockChanges(final ILightAccess lightAccess, final IChunkAccess atChunk, -+ final Set positions); ++ protected abstract void propagateBlockChanges(final ILightAccess lightAccess, final IChunkAccess atChunk, final Set positions); + + protected abstract void checkBlock(final int worldX, final int worldY, final int worldZ); + -+ // subclasses should not initialise caches, as this will always be done by the super call -+ // subclasses should not invoke updateVisible, as this will always be done by the super call -+ protected final void checkChunkEdges(final ILightAccess lightAccess, final IChunkAccess chunk) { ++ protected void checkChunkEdge(final ILightAccess lightAccess, final IChunkAccess chunk, ++ final int chunkX, final int chunkY, final int chunkZ) { ++ final SWMRNibbleArray currNibble = this.getNibbleFromCache(chunkX, chunkY, chunkZ); ++ if (currNibble == null) { ++ return; ++ } ++ ++ for (final AxisDirection direction : ONLY_HORIZONTAL_DIRECTIONS) { ++ final int neighbourOffX = direction.x; ++ final int neighbourOffZ = direction.z; ++ ++ final SWMRNibbleArray neighbourNibble = this.getNibbleFromCache(chunkX + neighbourOffX, ++ chunkY, chunkZ + neighbourOffZ); ++ ++ if (neighbourNibble == null) { ++ continue; ++ } ++ ++ if (!currNibble.isInitialisedUpdating() && !neighbourNibble.isInitialisedUpdating()) { ++ // both are zero, nothing to check. ++ continue; ++ } ++ ++ final int incX; ++ final int incZ; ++ final int startX; ++ final int startZ; ++ ++ if (neighbourOffX != 0) { ++ // x direction ++ incX = 0; ++ incZ = 1; ++ ++ if (direction.x < 0) { ++ // negative ++ startX = chunkX << 4; ++ } else { ++ startX = chunkX << 4 | 15; ++ } ++ startZ = chunkZ << 4; ++ } else { ++ // z direction ++ incX = 1; ++ incZ = 0; ++ ++ if (neighbourOffZ < 0) { ++ // negative ++ startZ = chunkZ << 4; ++ } else { ++ startZ = chunkZ << 4 | 15; ++ } ++ startX = chunkX << 4; ++ } ++ ++ for (int currY = chunkY << 4, maxY = currY | 15; currY <= maxY; ++currY) { ++ for (int i = 0, currX = startX, currZ = startZ; i < 16; ++i, currX += incX, currZ += incZ) { ++ final int neighbourX = currX + neighbourOffX; ++ final int neighbourZ = currZ + neighbourOffZ; ++ ++ final int currentLevel = currNibble.getUpdating((currX & 15) | ++ ((currZ & 15)) << 4 | ++ ((currY & 15) << 8) ++ ); ++ final int neighbourLevel = neighbourNibble.getUpdating((neighbourX & 15) | ++ ((neighbourZ & 15)) << 4 | ++ ((currY & 15) << 8) ++ ); ++ ++ if (currentLevel == neighbourLevel && (currentLevel == 0 || currentLevel == 15)) { ++ // nothing to check here ++ continue; ++ } ++ ++ if (Math.abs(currentLevel - neighbourLevel) == 1) { ++ final IBlockData currentBlock = this.getBlockState(currX, currY, currZ); ++ final IBlockData neighbourBlock = this.getBlockState(neighbourX, currY, neighbourZ); ++ ++ final int currentOpacity = currentBlock.getOpacityIfCached(); ++ final int neighbourOpacity = neighbourBlock.getOpacityIfCached(); ++ if (currentOpacity == 0 || currentOpacity == 1 || ++ neighbourOpacity == 0 || neighbourOpacity == 1) { ++ // looks good ++ continue; ++ } ++ } ++ ++ // setup queue, it looks like something could be inconsistent ++ this.checkBlock(currX, currY, currZ); ++ this.checkBlock(neighbourX, currY, neighbourZ); ++ } ++ } ++ } ++ } ++ ++ protected void checkChunkEdges(final ILightAccess lightAccess, final IChunkAccess chunk, final ShortCollection sections) { + final ChunkCoordIntPair chunkPos = chunk.getPos(); + final int chunkX = chunkPos.x; + final int chunkZ = chunkPos.z; + -+ for (int currSectionY = 16; currSectionY >= -1; --currSectionY) { -+ final SWMRNibbleArray currNibble = this.getNibbleFromCache(chunkX, currSectionY, chunkZ); -+ for (final Direction direction : ONLY_HORIZONTAL_DIRECTIONS) { -+ final int neighbourOffX = direction.x; -+ final int neighbourOffZ = direction.z; -+ -+ final SWMRNibbleArray neighbourNibble = this.getNibbleFromCache(chunkX + neighbourOffX, -+ currSectionY, chunkZ + neighbourOffZ); -+ -+ if (neighbourNibble == null) { -+ continue; -+ } -+ -+ if (!currNibble.isInitialisedUpdating() && !neighbourNibble.isInitialisedUpdating()) { -+ if (this.skylightPropagator) { -+ if (currNibble.isNullNibbleUpdating() == neighbourNibble.isNullNibbleUpdating()) { -+ continue; -+ } // else fall through to edge checks -+ } else { -+ continue; -+ } -+ } -+ -+ final int incX; -+ final int incZ; -+ final int startX; -+ final int startZ; -+ -+ if (neighbourOffX != 0) { -+ // x direction -+ incX = 0; -+ incZ = 1; -+ -+ if (direction.x < 0) { -+ // negative -+ startX = chunkX << 4; -+ } else { -+ startX = chunkX << 4 | 15; -+ } -+ startZ = chunkZ << 4; -+ } else { -+ // z direction -+ incX = 1; -+ incZ = 0; -+ -+ if (neighbourOffZ < 0) { -+ // negative -+ startZ = chunkZ << 4; -+ } else { -+ startZ = chunkZ << 4 | 15; -+ } -+ startX = chunkX << 4; -+ } -+ -+ for (int currY = currSectionY << 4, maxY = currY | 15; currY <= maxY; ++currY) { -+ for (int i = 0, currX = startX, currZ = startZ; i < 16; ++i, currX += incX, currZ += incZ) { -+ final int neighbourX = currX + neighbourOffX; -+ final int neighbourZ = currZ + neighbourOffZ; -+ -+ final int currentLevel = currNibble.getUpdating((currX & 15) | -+ ((currZ & 15)) << 4 | -+ ((currY & 15) << 8) -+ ); -+ final int neighbourLevel = neighbourNibble.getUpdating((neighbourX & 15) | -+ ((neighbourZ & 15)) << 4 | -+ ((currY & 15) << 8) -+ ); -+ -+ if (currentLevel == neighbourLevel && (currentLevel == 0 || currentLevel == 15)) { -+ // nothing to check here -+ continue; -+ } -+ -+ if (IntegerUtil.branchlessAbs(currentLevel - neighbourLevel) == 1) { -+ final IBlockData currentBlock = this.getBlockData(currX, currY, currZ); -+ final IBlockData neighbourBlock = this.getBlockData(neighbourX, currY, neighbourZ); -+ -+ final int currentOpacity = currentBlock.getOpacityIfCached(); -+ final int neighbourOpacity = neighbourBlock.getOpacityIfCached(); -+ if (currentOpacity == 0 || currentOpacity == 1 || -+ neighbourOpacity == 0 || neighbourOpacity == 1) { -+ // looks good -+ continue; -+ } -+ } -+ -+ // setup queue, it looks like something could be inconsistent -+ this.checkBlock(currX, currY, currZ); -+ this.checkBlock(neighbourX, currY, neighbourZ); -+ } -+ } -+ } ++ for (final ShortIterator iterator = sections.iterator(); iterator.hasNext();) { ++ this.checkChunkEdge(lightAccess, chunk, chunkX, iterator.nextShort(), chunkZ); + } + + this.performLightDecrease(lightAccess); + } + ++ // subclasses should not initialise caches, as this will always be done by the super call ++ // subclasses should not invoke updateVisible, as this will always be done by the super call ++ // verifies that light levels on this chunks edges are consistent with this chunk's neighbours ++ // edges. if they are not, they are decreased (effectively performing the logic in checkBlock). ++ // This does not resolve skylight source problems. ++ protected void checkChunkEdges(final ILightAccess lightAccess, final IChunkAccess chunk, final int fromSection, final int toSection) { ++ final ChunkCoordIntPair chunkPos = chunk.getPos(); ++ final int chunkX = chunkPos.x; ++ final int chunkZ = chunkPos.z; ++ ++ for (int currSectionY = toSection; currSectionY >= fromSection; --currSectionY) { ++ this.checkChunkEdge(lightAccess, chunk, chunkX, currSectionY, chunkZ); ++ } ++ ++ this.performLightDecrease(lightAccess); ++ } ++ ++ // pulls light from neighbours, and adds them into the increase queue. does not actually propagate. + protected final void propagateNeighbourLevels(final ILightAccess lightAccess, final IChunkAccess chunk, final int fromSection, final int toSection) { + final ChunkCoordIntPair chunkPos = chunk.getPos(); + final int chunkX = chunkPos.x; @@ -2667,27 +3337,21 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + + for (int currSectionY = toSection; currSectionY >= fromSection; --currSectionY) { + final SWMRNibbleArray currNibble = this.getNibbleFromCache(chunkX, currSectionY, chunkZ); -+ for (final Direction direction : ONLY_HORIZONTAL_DIRECTIONS) { ++ if (currNibble == null) { ++ continue; ++ } ++ for (final AxisDirection direction : ONLY_HORIZONTAL_DIRECTIONS) { + final int neighbourOffX = direction.x; + final int neighbourOffZ = direction.z; + + final SWMRNibbleArray neighbourNibble = this.getNibbleFromCache(chunkX + neighbourOffX, + currSectionY, chunkZ + neighbourOffZ); + -+ if (neighbourNibble == null) { ++ if (neighbourNibble == null || !neighbourNibble.isInitialisedUpdating()) { ++ // can't pull from 0 + continue; + } + -+ if (!neighbourNibble.isInitialisedUpdating()) { -+ if (this.skylightPropagator) { -+ if (currNibble.isNullNibbleUpdating() == neighbourNibble.isNullNibbleUpdating() || !neighbourNibble.isNullNibbleUpdating()) { -+ continue; -+ } // else fall through to edge checks -+ } else { -+ continue; -+ } -+ } -+ + final int incX; + final int incZ; + final int startX; @@ -2719,15 +3383,15 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + startX = chunkX << 4; + } + -+ final int propagateDirection = direction.getOpposite().ordinal() | 16; // we only want to check in this direction towards this chunk ++ final long propagateDirection = 1L << direction.getOpposite().ordinal(); // we only want to check in this direction towards this chunk + final int encodeOffset = this.coordinateOffset; + + for (int currY = currSectionY << 4, maxY = currY | 15; currY <= maxY; ++currY) { + for (int i = 0, currX = startX, currZ = startZ; i < 16; ++i, currX += incX, currZ += incZ) { + final int level = neighbourNibble.getUpdating( -+ (currX & 15) | -+ (currZ & 15) << 4 | -+ (currY & 15) << 8 ++ (currX & 15) ++ | ((currZ & 15) << 4) ++ | ((currY & 15) << 8) + ); + + if (level <= 1) { @@ -2735,20 +3399,89 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + continue; + } + -+ this.increaseQueue[this.increaseQueueInitialLength++] = (currX + (currZ << 6) + (currY << (6 + 6)) + encodeOffset) | -+ (level << (6 + 6 + 9)) | -+ ((propagateDirection) << (6 + 6 + 9 + 4)) | -+ FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; // don't know if the current block is transparent, must check. ++ this.increaseQueue[this.increaseQueueInitialLength++] = ++ ((currX + (currZ << 6) + (currY << (6 + 6)) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((level & 0xFL) << (6 + 6 + 16)) ++ | (propagateDirection << (6 + 6 + 16 + 4)) ++ | FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; // don't know if the current block is transparent, must check. + } + } + } + } + } + -+ public final void checkChunkEdges(final ILightAccess lightAccess, final int chunkX, final int chunkZ) { -+ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, false); ++ public static Boolean[] getEmptySectionsForChunk(final IChunkAccess chunk) { ++ final Boolean[] ret = new Boolean[16]; ++ ++ final ChunkSection[] sections = chunk.getSections(); ++ ++ for (int i = 0; i < sections.length; ++i) { ++ if (sections[i] == null || sections[i].isFullOfAir()) { ++ ret[i] = Boolean.TRUE; ++ } else { ++ ret[i] = Boolean.FALSE; ++ } ++ } ++ ++ return ret; ++ } ++ ++ public final void handleEmptySectionChanges(final ILightAccess lightAccess, final int chunkX, final int chunkZ, ++ final Boolean[] emptinessChanges) { ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, this.isClientSide); ++ if (this.isClientSide) { ++ // force current chunk into cache ++ final IChunkAccess chunk = (IChunkAccess)lightAccess.getFeaturesReadyChunk(chunkX, chunkZ); ++ if (chunk == null) { ++ // unloaded this frame (or last), and we were still queued ++ return; ++ } ++ this.setChunkInCache(chunkX, chunkZ, chunk); ++ this.setBlocksForChunkInCache(chunkX, chunkZ, chunk.getSections()); ++ this.setNibblesForChunkInCache(chunkX, chunkZ, this.getNibblesOnChunk(chunk)); ++ this.setEmptinessMapCache(chunkX, chunkZ, this.getEmptinessMap(chunk)); ++ } + try { -+ this.checkChunkEdges(lightAccess, this.getChunkInCache(chunkX, chunkZ)); ++ final IChunkAccess chunk = this.getChunkInCache(chunkX, chunkZ); ++ if (chunk == null) { ++ return; ++ } ++ this.handleEmptySectionChanges(lightAccess, chunk, emptinessChanges, false); ++ this.updateVisible(lightAccess); ++ } finally { ++ this.destroyCaches(); ++ } ++ } ++ ++ // subclasses should not initialise caches, as this will always be done by the super call ++ // subclasses should not invoke updateVisible, as this will always be done by the super call ++ // subclasses are guaranteed that this is always called before a changed block set ++ // newChunk specifies whether the changes describe a "first load" of a chunk or changes to existing, already loaded chunks ++ protected abstract void handleEmptySectionChanges(final ILightAccess lightAccess, final IChunkAccess chunk, ++ final Boolean[] emptinessChanges, final boolean unlit); ++ ++ public final void checkChunkEdges(final ILightAccess lightAccess, final int chunkX, final int chunkZ) { ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true); ++ try { ++ final IChunkAccess chunk = this.getChunkInCache(chunkX, chunkZ); ++ if (chunk == null) { ++ return; ++ } ++ this.checkChunkEdges(lightAccess, chunk, -1, 16); ++ this.updateVisible(lightAccess); ++ } finally { ++ this.destroyCaches(); ++ } ++ } ++ ++ public final void checkChunkEdges(final ILightAccess lightAccess, final int chunkX, final int chunkZ, final ShortCollection sections) { ++ this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, true); ++ try { ++ final IChunkAccess chunk = this.getChunkInCache(chunkX, chunkZ); ++ if (chunk == null) { ++ return; ++ } ++ this.checkChunkEdges(lightAccess, chunk, sections); + this.updateVisible(lightAccess); + } finally { + this.destroyCaches(); @@ -2759,17 +3492,21 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + // subclasses should not invoke updateVisible, as this will always be done by the super call + // needsEdgeChecks applies when possibly loading vanilla data, which means we need to validate the current + // chunks light values with respect to neighbours ++ // subclasses should note that the emptiness changes are propagated BEFORE this is called, so this function ++ // does not need to detect empty chunks itself (and it should do no handling for them either!) + protected abstract void lightChunk(final ILightAccess lightAccess, final IChunkAccess chunk, final boolean needsEdgeChecks); + -+ public final void light(final ILightAccess lightAccess, final int chunkX, final int chunkZ) { ++ public final void light(final ILightAccess lightAccess, final int chunkX, final int chunkZ, final Boolean[] emptySections) { + this.setupCaches(lightAccess, chunkX * 16 + 7, 128, chunkZ * 16 + 7, false); + // force current chunk into cache + final IChunkAccess chunk = (IChunkAccess)lightAccess.getFeaturesReadyChunk(chunkX, chunkZ); + this.setChunkInCache(chunkX, chunkZ, chunk); + this.setBlocksForChunkInCache(chunkX, chunkZ, chunk.getSections()); + this.setNibblesForChunkInCache(chunkX, chunkZ, this.getNibblesOnChunk(chunk)); ++ this.setEmptinessMapCache(chunkX, chunkZ, this.getEmptinessMap(chunk)); + + try { ++ this.handleEmptySectionChanges(lightAccess, chunk, emptySections, true); + this.lightChunk(lightAccess, chunk, false); + this.updateVisible(lightAccess); + } finally { @@ -2777,9 +3514,9 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + } + } + -+ public final void relight(final ILightAccess world, final int chunkX, final int chunkZ) { -+ final IChunkAccess chunk = (IChunkAccess)world.getFeaturesReadyChunk(chunkX, chunkZ); -+ this.relightChunk(world, chunk); ++ public final void relight(final ILightAccess lightAccess, final int chunkX, final int chunkZ) { ++ final IChunkAccess chunk = (IChunkAccess)lightAccess.getFeaturesReadyChunk(chunkX, chunkZ); ++ this.relightChunk(lightAccess, chunk); + } + + protected final void relightChunk(final ILightAccess lightAccess, final IChunkAccess chunk) { @@ -2787,10 +3524,14 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + this.setupEncodeOffset(chunkPos.x * 16 + 7, 128, chunkPos.z * 16 + 7); + + try { ++ final SWMRNibbleArray[] chunkNibbles = getFilledEmptyLight(); ++ + this.setChunkInCache(chunkPos.x, chunkPos.z, chunk); + this.setBlocksForChunkInCache(chunkPos.x, chunkPos.z, chunk.getSections()); -+ final SWMRNibbleArray[] chunkNibbles = getFilledEmptyLight(this.skylightPropagator); + this.setNibblesForChunkInCache(chunkPos.x, chunkPos.z, chunkNibbles); ++ this.setEmptinessMapCache(chunkPos.x, chunkPos.z, new boolean[9][]); ++ ++ this.handleEmptySectionChanges(lightAccess, chunk, getEmptySectionsForChunk(chunk), true); + this.lightChunk(lightAccess, chunk, false); + + for (int dz = -1; dz <= 1; ++dz) { @@ -2809,7 +3550,10 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + + this.setChunkInCache(cx, cz, neighbourChunk); + this.setBlocksForChunkInCache(cx, cz, neighbourChunk.getSections()); -+ this.setNibblesForChunkInCache(cx, cz, getFilledEmptyLight(this.skylightPropagator)); ++ this.setNibblesForChunkInCache(cx, cz, getFilledEmptyLight()); ++ this.setEmptinessMapCache(cx, cz, new boolean[9][]); ++ ++ this.handleEmptySectionChanges(lightAccess, neighbourChunk, getEmptySectionsForChunk(neighbourChunk), true); + this.lightChunk(lightAccess, neighbourChunk, false); + } + } @@ -2825,38 +3569,39 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + // this is also the basic algorithm, the optimised algorithm is always going to be tested against this one + // and this one is always tested against vanilla + // contains: -+ // lower 21 bits: encoded coordinate position (x | (z << 6) | (y << (6 + 6)))) ++ // lower (6 + 6 + 16) = 28 bits: encoded coordinate position (x | (z << 6) | (y << (6 + 6)))) + // next 4 bits: propagated light level (0, 15] -+ // next 5 bits: direction propagated from -+ // next 0 bits: unused -+ // last 2 bits: state flags ++ // next 6 bits: propagation direction bitset ++ // next 24 bits: unused ++ // last 4 bits: state flags + // state flags: ++ // whether the propagation must set the current position's light value (0 if decrease, propagated light level if increase) + // whether the propagation needs to check if its current level is equal to the expected level + // used only in increase propagation -+ protected static final int FLAG_RECHECK_LEVEL = Integer.MIN_VALUE >>> 1; ++ protected static final long FLAG_RECHECK_LEVEL = Long.MIN_VALUE >>> 1; + // whether the propagation needs to consider if its block is conditionally transparent -+ protected static final int FLAG_HAS_SIDED_TRANSPARENT_BLOCKS = Integer.MIN_VALUE; ++ protected static final long FLAG_HAS_SIDED_TRANSPARENT_BLOCKS = Long.MIN_VALUE; + -+ protected final int[] increaseQueue = new int[16 * 16 * (16 * (16 + 2)) * 9 + 1]; ++ protected final long[] increaseQueue = new long[16 * 16 * (16 * (16 + 2)) * 9 + 1]; + protected int increaseQueueInitialLength; -+ protected final int[] decreaseQueue = new int[16 * 16 * (16 * (16 + 2)) * 9 + 1]; ++ protected final long[] decreaseQueue = new long[16 * 16 * (16 * (16 + 2)) * 9 + 1]; + protected int decreaseQueueInitialLength; + -+ protected static final Direction[][] OLD_CHECK_DIRECTIONS = new Direction[4 * 8][]; ++ protected static final AxisDirection[][] OLD_CHECK_DIRECTIONS = new AxisDirection[1 << 6][]; ++ protected static final int ALL_DIRECTIONS_BITSET = (1 << 6) - 1; + static { -+ for (int i = 0; i < AXIS_DIRECTIONS.length; ++i) { -+ final Direction direction = AXIS_DIRECTIONS[i]; -+ final List directions = new ArrayList<>(Arrays.asList(AXIS_DIRECTIONS)); -+ directions.remove(direction.getOpposite()); -+ OLD_CHECK_DIRECTIONS[direction.ordinal()] = directions.toArray(new Direction[0]); -+ OLD_CHECK_DIRECTIONS[direction.ordinal() | 8] = AXIS_DIRECTIONS; // flag ALL_DIRECTIONS -+ OLD_CHECK_DIRECTIONS[direction.ordinal() | 16] = new Direction[] { direction }; // flag ONLY_THIS_DIRECTION ++ for (int i = 0; i < OLD_CHECK_DIRECTIONS.length; ++i) { ++ final List directions = new ArrayList<>(); ++ for (int bitset = i, len = Integer.bitCount(i), index = 0; index < len; ++index, bitset ^= IntegerUtil.getTrailingBit(bitset)) { ++ directions.add(AXIS_DIRECTIONS[IntegerUtil.trailingZeros(bitset)]); ++ } ++ OLD_CHECK_DIRECTIONS[i] = directions.toArray(new AxisDirection[0]); + } + } + + protected final void performLightIncrease(final ILightAccess lightAccess) { + final IBlockAccess world = lightAccess.getWorld(); -+ final int[] queue = this.increaseQueue; ++ final long[] queue = this.increaseQueue; + int queueReadIndex = 0; + int queueLength = this.increaseQueueInitialLength; + this.increaseQueueInitialLength = 0; @@ -2867,25 +3612,24 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + final int sectionOffset = this.chunkSectionIndexOffset; + + while (queueReadIndex < queueLength) { -+ final int queueValue = queue[queueReadIndex++]; ++ final long queueValue = queue[queueReadIndex++]; + -+ final int posX = ((queueValue & 63) + decodeOffsetX); -+ final int posZ = (((queueValue >>> 6) & 63) + decodeOffsetZ); -+ final int posY = (((queueValue >>> 12) & 511) + decodeOffsetY); -+ final int propagatedLightLevel = ((queueValue >>> (6 + 6 + 9)) & 0xF); -+ final int fromDirection = ((queueValue >>> (6 + 6 + 9 + 4)) & 0x1F); -+ final Direction[] checkDirections = OLD_CHECK_DIRECTIONS[fromDirection]; ++ final int posX = ((int)queueValue & 63) + decodeOffsetX; ++ final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; ++ final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; ++ final int propagatedLightLevel = (int)((queueValue >>> (6 + 6 + 16)) & 0xFL); ++ final AxisDirection[] checkDirections = OLD_CHECK_DIRECTIONS[(int)((queueValue >>> (6 + 6 + 16 + 4)) & 63L)]; + -+ if ((queueValue & FLAG_RECHECK_LEVEL) != 0) { ++ if ((queueValue & FLAG_RECHECK_LEVEL) != 0L) { + if (this.getLightLevel(posX, posY, posZ) != propagatedLightLevel) { + // not at the level we expect, so something changed. + continue; + } + } + -+ if ((queueValue & FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) == 0) { ++ if ((queueValue & FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) == 0L) { + // we don't need to worry about our state here. -+ for (final Direction propagate : checkDirections) { ++ for (final AxisDirection propagate : checkDirections) { + final int offX = posX + propagate.x; + final int offY = posY + propagate.y; + final int offZ = posZ + propagate.z; @@ -2893,35 +3637,38 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset; + final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8); + -+ final int currentLevel = this.getLightLevel(sectionIndex, localIndex); -+ -+ if (currentLevel >= (propagatedLightLevel - 1)) { -+ continue; // already at the level we want ++ final SWMRNibbleArray currentNibble = this.nibbleCache[sectionIndex]; ++ final int currentLevel; ++ if (currentNibble == null || (currentLevel = currentNibble.getUpdating(localIndex)) >= (propagatedLightLevel - 1)) { ++ continue; // already at the level we want or unloaded + } + -+ final IBlockData blockData = this.getBlockData(sectionIndex, localIndex); -+ if (blockData == null) { ++ final IBlockData blockState = this.getBlockState(sectionIndex, localIndex); ++ if (blockState == null) { + continue; + } -+ final int opacityCached = blockData.getOpacityIfCached(); ++ final int opacityCached = blockState.getOpacityIfCached(); + if (opacityCached != -1) { + final int targetLevel = propagatedLightLevel - Math.max(1, opacityCached); + if (targetLevel > currentLevel) { -+ this.setLightLevel(sectionIndex, localIndex, targetLevel); ++ ++ currentNibble.set(localIndex, targetLevel); ++ this.postLightUpdate(offX, offY, offZ); ++ + if (targetLevel > 1) { + queue[queueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (targetLevel << (6 + 6 + 9)) -+ | (propagate.ordinal() << (6 + 6 + 9 + 4)); ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4)); + continue; + } + } + continue; + } else { + this.mutablePos1.setValues(offX, offY, offZ); -+ int flags = 0; -+ if (blockData.isConditionallyFullOpaque()) { -+ final VoxelShape cullingFace = blockData.getCullingFace(world, this.mutablePos1, propagate.getOpposite().nms); ++ long flags = 0; ++ if (blockState.isConditionallyFullOpaque()) { ++ final VoxelShape cullingFace = blockState.getCullingFace(world, this.mutablePos1, propagate.getOpposite().nms); + + if (VoxelShapes.combinationOccludes(VoxelShapes.getEmptyShape(), cullingFace)) { + continue; @@ -2929,17 +3676,20 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; + } + -+ final int opacity = blockData.getOpacity(world, this.mutablePos1); ++ final int opacity = blockState.getOpacity(world, this.mutablePos1); + final int targetLevel = propagatedLightLevel - Math.max(1, opacity); + if (targetLevel <= currentLevel) { + continue; + } -+ this.setLightLevel(sectionIndex, localIndex, targetLevel); ++ ++ currentNibble.set(localIndex, targetLevel); ++ this.postLightUpdate(offX, offY, offZ); ++ + if (targetLevel > 1) { + queue[queueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (targetLevel << (6 + 6 + 9)) -+ | (propagate.ordinal() << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4)) + | (flags); + } + continue; @@ -2947,14 +3697,14 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + } + } else { + // we actually need to worry about our state here -+ final IBlockData fromBlock = this.getBlockData(posX, posY, posZ); ++ final IBlockData fromBlock = this.getBlockState(posX, posY, posZ); + this.mutablePos2.setValues(posX, posY, posZ); -+ for (final Direction propagate : checkDirections) { ++ for (final AxisDirection propagate : checkDirections) { + final int offX = posX + propagate.x; + final int offY = posY + propagate.y; + final int offZ = posZ + propagate.z; + -+ final VoxelShape fromShape = (fromBlock.isConditionallyFullOpaque()) ? fromBlock.getCullingFace(world, this.mutablePos2, propagate.nms) : VoxelShapes.getEmptyShape(); ++ final VoxelShape fromShape = fromBlock.isConditionallyFullOpaque() ? fromBlock.getCullingFace(world, this.mutablePos2, propagate.nms) : VoxelShapes.getEmptyShape(); + + if (fromShape != VoxelShapes.getEmptyShape() && VoxelShapes.combinationOccludes(VoxelShapes.getEmptyShape(), fromShape)) { + continue; @@ -2963,35 +3713,39 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset; + final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8); + -+ final int currentLevel = this.getLightLevel(sectionIndex, localIndex); ++ final SWMRNibbleArray currentNibble = this.nibbleCache[sectionIndex]; ++ final int currentLevel; + -+ if (currentLevel >= (propagatedLightLevel - 1)) { ++ if (currentNibble == null || (currentLevel = currentNibble.getUpdating(localIndex)) >= (propagatedLightLevel - 1)) { + continue; // already at the level we want + } + -+ final IBlockData blockData = this.getBlockData(sectionIndex, localIndex); -+ if (blockData == null) { ++ final IBlockData blockState = this.getBlockState(sectionIndex, localIndex); ++ if (blockState == null) { + continue; + } -+ final int opacityCached = blockData.getOpacityIfCached(); ++ final int opacityCached = blockState.getOpacityIfCached(); + if (opacityCached != -1) { + final int targetLevel = propagatedLightLevel - Math.max(1, opacityCached); + if (targetLevel > currentLevel) { -+ this.setLightLevel(sectionIndex, localIndex, targetLevel); ++ ++ currentNibble.set(localIndex, targetLevel); ++ this.postLightUpdate(offX, offY, offZ); ++ + if (targetLevel > 1) { + queue[queueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (targetLevel << (6 + 6 + 9)) -+ | (propagate.ordinal() << (6 + 6 + 9 + 4)); ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4)); + continue; + } + } + continue; + } else { + this.mutablePos1.setValues(offX, offY, offZ); -+ int flags = 0; -+ if (blockData.isConditionallyFullOpaque()) { -+ final VoxelShape cullingFace = blockData.getCullingFace(world, this.mutablePos1, propagate.getOpposite().nms); ++ long flags = 0; ++ if (blockState.isConditionallyFullOpaque()) { ++ final VoxelShape cullingFace = blockState.getCullingFace(world, this.mutablePos1, propagate.getOpposite().nms); + + if (VoxelShapes.combinationOccludes(fromShape, cullingFace)) { + continue; @@ -2999,17 +3753,20 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; + } + -+ final int opacity = blockData.getOpacity(world, this.mutablePos1); ++ final int opacity = blockState.getOpacity(world, this.mutablePos1); + final int targetLevel = propagatedLightLevel - Math.max(1, opacity); + if (targetLevel <= currentLevel) { + continue; + } -+ this.setLightLevel(sectionIndex, localIndex, targetLevel); ++ ++ currentNibble.set(localIndex, targetLevel); ++ this.postLightUpdate(offX, offY, offZ); ++ + if (targetLevel > 1) { + queue[queueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (targetLevel << (6 + 6 + 9)) -+ | (propagate.ordinal() << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | (propagate.everythingButTheOppositeDirection << (6 + 6 + 16 + 4)) + | (flags); + } + continue; @@ -3021,8 +3778,8 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + + protected final void performLightDecrease(final ILightAccess lightAccess) { + final IBlockAccess world = lightAccess.getWorld(); -+ final int[] queue = this.decreaseQueue; -+ final int[] increaseQueue = this.increaseQueue; ++ final long[] queue = this.decreaseQueue; ++ final long[] increaseQueue = this.increaseQueue; + int queueReadIndex = 0; + int queueLength = this.decreaseQueueInitialLength; + this.decreaseQueueInitialLength = 0; @@ -3035,18 +3792,17 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + final int emittedMask = this.emittedLightMask; + + while (queueReadIndex < queueLength) { -+ final int queueValue = queue[queueReadIndex++]; ++ final long queueValue = queue[queueReadIndex++]; + -+ final int posX = ((queueValue & 63) + decodeOffsetX); -+ final int posZ = (((queueValue >>> 6) & 63) + decodeOffsetZ); -+ final int posY = (((queueValue >>> 12) & 511) + decodeOffsetY); -+ final int propagatedLightLevel = ((queueValue >>> (6 + 6 + 9)) & 0xF); -+ final int fromDirection = ((queueValue >>> (6 + 6 + 9 + 4)) & 0x1F); -+ final Direction[] checkDirections = OLD_CHECK_DIRECTIONS[fromDirection]; ++ final int posX = ((int)queueValue & 63) + decodeOffsetX; ++ final int posZ = (((int)queueValue >>> 6) & 63) + decodeOffsetZ; ++ final int posY = (((int)queueValue >>> 12) & ((1 << 16) - 1)) + decodeOffsetY; ++ final int propagatedLightLevel = (int)((queueValue >>> (6 + 6 + 16)) & 0xF); ++ final AxisDirection[] checkDirections = OLD_CHECK_DIRECTIONS[(int)((queueValue >>> (6 + 6 + 16 + 4)) & 63)]; + -+ if ((queueValue & FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) == 0) { ++ if ((queueValue & FLAG_HAS_SIDED_TRANSPARENT_BLOCKS) == 0L) { + // we don't need to worry about our state here. -+ for (final Direction propagate : checkDirections) { ++ for (final AxisDirection propagate : checkDirections) { + final int offX = posX + propagate.x; + final int offY = posY + propagate.y; + final int offZ = posZ + propagate.z; @@ -3054,50 +3810,56 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + final int sectionIndex = (offX >> 4) + 5 * (offZ >> 4) + (5 * 5) * (offY >> 4) + sectionOffset; + final int localIndex = (offX & 15) | ((offZ & 15) << 4) | ((offY & 15) << 8); + -+ final int lightLevel = this.getLightLevel(sectionIndex, localIndex); ++ final SWMRNibbleArray currentNibble = this.nibbleCache[sectionIndex]; ++ final int lightLevel; + -+ if (lightLevel == 0) { -+ // already at lowest, nothing we can do ++ if (currentNibble == null || (lightLevel = currentNibble.getUpdating(localIndex)) == 0) { ++ // already at lowest (or unloaded), nothing we can do + continue; + } + -+ final IBlockData blockData = this.getBlockData(sectionIndex, localIndex); -+ final int opacityCached = blockData.getOpacityIfCached(); -+ // no null check, blockData cannot be null if level != 0 ++ final IBlockData blockState = this.getBlockState(sectionIndex, localIndex); ++ if (blockState == null) { ++ continue; ++ } ++ final int opacityCached = blockState.getOpacityIfCached(); + if (opacityCached != -1) { + final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacityCached)); + if (lightLevel > targetLevel) { + // it looks like another source propagated here, so re-propagate it + increaseQueue[increaseQueueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (lightLevel << (6 + 6 + 9)) -+ | ((propagate.ordinal() | 8) << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((lightLevel & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) + | FLAG_RECHECK_LEVEL; + continue; + } -+ final int emittedLight = blockData.getEmittedLight() & emittedMask; ++ final int emittedLight = blockState.getEmittedLight() & emittedMask; + if (emittedLight != 0) { + // re-propagate source + increaseQueue[increaseQueueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (emittedLight << (6 + 6 + 9)) -+ | ((propagate.ordinal() | 8) << (6 + 6 + 9 + 4)) -+ | (blockData.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0); ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((emittedLight & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (blockState.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0L); + } -+ this.setLightLevel(sectionIndex, localIndex, emittedLight); ++ ++ currentNibble.set(localIndex, emittedLight); ++ this.postLightUpdate(offX, offY, offZ); ++ + if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour... + queue[queueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (targetLevel << (6 + 6 + 9)) -+ | (propagate.ordinal() << (6 + 6 + 9 + 4)); ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4)); + continue; + } + continue; + } else { + this.mutablePos1.setValues(offX, offY, offZ); -+ int flags = 0; -+ if (blockData.isConditionallyFullOpaque()) { -+ final VoxelShape cullingFace = blockData.getCullingFace(world, this.mutablePos1, propagate.getOpposite().nms); ++ long flags = 0; ++ if (blockState.isConditionallyFullOpaque()) { ++ final VoxelShape cullingFace = blockState.getCullingFace(world, this.mutablePos1, propagate.getOpposite().nms); + + if (VoxelShapes.combinationOccludes(VoxelShapes.getEmptyShape(), cullingFace)) { + continue; @@ -3105,32 +3867,35 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; + } + -+ final int opacity = blockData.getOpacity(world, this.mutablePos1); ++ final int opacity = blockState.getOpacity(world, this.mutablePos1); + final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity)); + if (lightLevel > targetLevel) { + // it looks like another source propagated here, so re-propagate it + increaseQueue[increaseQueueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (lightLevel << (6 + 6 + 9)) -+ | ((propagate.ordinal() | 8) << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((lightLevel & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) + | (FLAG_RECHECK_LEVEL | flags); + continue; + } -+ final int emittedLight = blockData.getEmittedLight() & emittedMask; ++ final int emittedLight = blockState.getEmittedLight() & emittedMask; + if (emittedLight != 0) { + // re-propagate source + increaseQueue[increaseQueueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (emittedLight << (6 + 6 + 9)) -+ | ((propagate.ordinal() | 8) << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((emittedLight & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) + | flags; + } -+ this.setLightLevel(sectionIndex, localIndex, emittedLight); ++ ++ currentNibble.set(localIndex, emittedLight); ++ this.postLightUpdate(offX, offY, offZ); ++ + if (targetLevel > 0) { + queue[queueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (targetLevel << (6 + 6 + 9)) -+ | (propagate.ordinal() << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4)) + | flags; + } + continue; @@ -3138,9 +3903,9 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + } + } else { + // we actually need to worry about our state here -+ final IBlockData fromBlock = this.getBlockData(posX, posY, posZ); ++ final IBlockData fromBlock = this.getBlockState(posX, posY, posZ); + this.mutablePos2.setValues(posX, posY, posZ); -+ for (final Direction propagate : checkDirections) { ++ for (final AxisDirection propagate : checkDirections) { + final int offX = posX + propagate.x; + final int offY = posY + propagate.y; + final int offZ = posZ + propagate.z; @@ -3154,50 +3919,56 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + continue; + } + -+ final int lightLevel = this.getLightLevel(sectionIndex, localIndex); ++ final SWMRNibbleArray currentNibble = this.nibbleCache[sectionIndex]; ++ final int lightLevel; + -+ if (lightLevel == 0) { -+ // already at lowest, nothing we can do ++ if (currentNibble == null || (lightLevel = currentNibble.getUpdating(localIndex)) == 0) { ++ // already at lowest (or unloaded), nothing we can do + continue; + } + -+ final IBlockData blockData = this.getBlockData(sectionIndex, localIndex); -+ final int opacityCached = blockData.getOpacityIfCached(); -+ // no null check, blockData cannot be null if level != 0 ++ final IBlockData blockState = this.getBlockState(sectionIndex, localIndex); ++ if (blockState == null) { ++ continue; ++ } ++ final int opacityCached = blockState.getOpacityIfCached(); + if (opacityCached != -1) { + final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacityCached)); + if (lightLevel > targetLevel) { + // it looks like another source propagated here, so re-propagate it + increaseQueue[increaseQueueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (lightLevel << (6 + 6 + 9)) -+ | ((propagate.ordinal() | 8) << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((lightLevel & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) + | FLAG_RECHECK_LEVEL; + continue; + } -+ final int emittedLight = blockData.getEmittedLight() & emittedMask; ++ final int emittedLight = blockState.getEmittedLight() & emittedMask; + if (emittedLight != 0) { + // re-propagate source + increaseQueue[increaseQueueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (emittedLight << (6 + 6 + 9)) -+ | ((propagate.ordinal() | 8) << (6 + 6 + 9 + 4)) -+ | (blockData.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0); ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((emittedLight & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) ++ | (blockState.isConditionallyFullOpaque() ? FLAG_HAS_SIDED_TRANSPARENT_BLOCKS : 0L); + } -+ this.setLightLevel(sectionIndex, localIndex, emittedLight); ++ ++ currentNibble.set(localIndex, emittedLight); ++ this.postLightUpdate(offX, offY, offZ); ++ + if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour... + queue[queueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (targetLevel << (6 + 6 + 9)) -+ | (propagate.ordinal() << (6 + 6 + 9 + 4)); ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4)); + continue; + } + continue; + } else { + this.mutablePos1.setValues(offX, offY, offZ); -+ int flags = 0; -+ if (blockData.isConditionallyFullOpaque()) { -+ final VoxelShape cullingFace = blockData.getCullingFace(world, this.mutablePos1, propagate.getOpposite().nms); ++ long flags = 0; ++ if (blockState.isConditionallyFullOpaque()) { ++ final VoxelShape cullingFace = blockState.getCullingFace(world, this.mutablePos1, propagate.getOpposite().nms); + + if (VoxelShapes.combinationOccludes(fromShape, cullingFace)) { + continue; @@ -3205,32 +3976,35 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + flags |= FLAG_HAS_SIDED_TRANSPARENT_BLOCKS; + } + -+ final int opacity = blockData.getOpacity(world, this.mutablePos1); ++ final int opacity = blockState.getOpacity(world, this.mutablePos1); + final int targetLevel = Math.max(0, propagatedLightLevel - Math.max(1, opacity)); + if (lightLevel > targetLevel) { + // it looks like another source propagated here, so re-propagate it + increaseQueue[increaseQueueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (lightLevel << (6 + 6 + 9)) -+ | ((propagate.ordinal() | 8) << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((lightLevel & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) + | (FLAG_RECHECK_LEVEL | flags); + continue; + } -+ final int emittedLight = blockData.getEmittedLight() & emittedMask; ++ final int emittedLight = blockState.getEmittedLight() & emittedMask; + if (emittedLight != 0) { + // re-propagate source + increaseQueue[increaseQueueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (emittedLight << (6 + 6 + 9)) -+ | ((propagate.ordinal() | 8) << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((emittedLight & 0xFL) << (6 + 6 + 16)) ++ | (((long)ALL_DIRECTIONS_BITSET) << (6 + 6 + 16 + 4)) + | flags; + } -+ this.setLightLevel(sectionIndex, localIndex, emittedLight); ++ ++ currentNibble.set(localIndex, emittedLight); ++ this.postLightUpdate(offX, offY, offZ); ++ + if (targetLevel > 0) { // we actually need to propagate 0 just in case we find a neighbour... + queue[queueLength++] = -+ (offX + (offZ << 6) + (offY << 12) + encodeOffset) -+ | (targetLevel << (6 + 6 + 9)) -+ | (propagate.ordinal() << (6 + 6 + 9 + 4)) ++ ((offX + (offZ << 6) + (offY << 12) + encodeOffset) & ((1L << (6 + 6 + 16)) - 1)) ++ | ((targetLevel & 0xFL) << (6 + 6 + 16)) ++ | ((propagate.everythingButTheOppositeDirection) << (6 + 6 + 16 + 4)) + | flags; + } + continue; @@ -3244,46 +4018,228 @@ index 0000000000000000000000000000000000000000..7fcbfa5c224426540693424c34a517bd + this.performLightIncrease(lightAccess); + } +} -diff --git a/src/main/java/com/tuinity/tuinity/chunk/light/ThreadedStarLightEngine.java b/src/main/java/com/tuinity/tuinity/chunk/light/ThreadedStarLightEngine.java +diff --git a/src/main/java/com/tuinity/tuinity/chunk/light/StarLightInterface.java b/src/main/java/com/tuinity/tuinity/chunk/light/StarLightInterface.java new file mode 100644 -index 0000000000000000000000000000000000000000..767831d169710388613396fcf8845453ef09f09d +index 0000000000000000000000000000000000000000..86ea3596a435efd6e8d93a755aea2159dcf96668 --- /dev/null -+++ b/src/main/java/com/tuinity/tuinity/chunk/light/ThreadedStarLightEngine.java -@@ -0,0 +1,181 @@ ++++ b/src/main/java/com/tuinity/tuinity/chunk/light/StarLightInterface.java +@@ -0,0 +1,446 @@ +package com.tuinity.tuinity.chunk.light; + ++import com.tuinity.tuinity.util.CoordinateUtils; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; ++import it.unimi.dsi.fastutil.shorts.ShortCollection; +import net.minecraft.server.BlockPosition; ++import net.minecraft.server.ChunkStatus; ++import net.minecraft.server.IChunkAccess; +import net.minecraft.server.ILightAccess; -+import net.minecraft.server.MCUtil; ++import net.minecraft.server.LightEngineLayerEventListener; ++import net.minecraft.server.NibbleArray; ++import net.minecraft.server.SectionPosition; +import net.minecraft.server.WorldServer; +import java.util.ArrayDeque; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + -+public final class ThreadedStarLightEngine { ++public final class StarLightInterface { + ++ ++ /** ++ * Can be {@code null}, indicating the light is all empty. ++ */ + protected final WorldServer world; + protected final ILightAccess lightAccess; + + protected final ArrayDeque cachedSkyPropagators; + protected final ArrayDeque cachedBlockPropagators; + -+ protected final Long2ObjectOpenHashMap> changedBlocks = new Long2ObjectOpenHashMap<>(); ++ protected final Long2ObjectOpenHashMap changedBlocks = new Long2ObjectOpenHashMap<>(); + -+ public ThreadedStarLightEngine(final ILightAccess lightAccess, final boolean hasSkyLight, final boolean hasBlockLight) { ++ protected final LightEngineLayerEventListener skyReader; ++ protected final LightEngineLayerEventListener blockReader; ++ protected static final boolean isClientSide = false; ++ ++ public StarLightInterface(final ILightAccess lightAccess, final boolean hasSkyLight, final boolean hasBlockLight) { + this.lightAccess = lightAccess; -+ this.world = (WorldServer)lightAccess.getWorld(); -+ this.cachedSkyPropagators = hasSkyLight ? new ArrayDeque<>() : null; -+ this.cachedBlockPropagators = hasBlockLight ? new ArrayDeque<>() : null; ++ this.world = lightAccess == null ? null : (WorldServer)lightAccess.getWorld(); ++ this.cachedSkyPropagators = hasSkyLight && lightAccess != null ? new ArrayDeque<>() : null; ++ this.cachedBlockPropagators = hasBlockLight && lightAccess != null ? new ArrayDeque<>() : null; ++ this.skyReader = !hasSkyLight ? LightEngineLayerEventListener.Void.INSTANCE : new LightEngineLayerEventListener() { ++ @Override ++ public NibbleArray a(final SectionPosition pos) { ++ final IChunkAccess chunk = StarLightInterface.this.getAnyChunkNow(pos.getX(), pos.getZ()); ++ if (chunk == null || (!StarLightInterface.this.isClientSide && !chunk.isLit()) || !chunk.getChunkStatus().isAtLeastStatus(ChunkStatus.LIGHT)) { ++ return null; ++ } ++ ++ final int sectionY = pos.getY(); ++ ++ if (sectionY > 16 || sectionY < -1) { ++ return null; ++ } ++ ++ if (chunk.getEmptinessMap()[IChunkAccess.getEmptinessMapIndex(0, 0)] == null) { ++ return null; ++ } ++ ++ return chunk.getSkyNibbles()[sectionY + 1].toVanillaNibble(); ++ } ++ ++ @Override ++ public int b(final BlockPosition blockPos) { ++ final int x = blockPos.getX(); ++ int y = blockPos.getY(); ++ final int z = blockPos.getZ(); ++ ++ final IChunkAccess chunk = StarLightInterface.this.getAnyChunkNow(x >> 4, z >> 4); ++ if (chunk == null || (!StarLightInterface.this.isClientSide && !chunk.isLit()) || !chunk.getChunkStatus().isAtLeastStatus(ChunkStatus.LIGHT)) { ++ return 15; ++ } ++ ++ int sectionY = y >> 4; ++ ++ if (sectionY > 16) { ++ return 15; ++ } ++ ++ if (sectionY < -1) { ++ y = -16; ++ sectionY = -1; ++ } ++ ++ final SWMRNibbleArray[] nibbles = chunk.getSkyNibbles(); ++ final SWMRNibbleArray immediate = nibbles[sectionY + 1]; ++ ++ if (StarLightInterface.this.isClientSide) { ++ if (!immediate.isNullNibbleUpdating()) { ++ return immediate.getUpdating(x, y, z); ++ } ++ } else { ++ if (!immediate.isNullNibbleVisible()) { ++ return immediate.getVisible(x, y, z); ++ } ++ } ++ ++ final boolean[] emptinessMap = chunk.getEmptinessMap()[IChunkAccess.getEmptinessMapIndex(0, 0)]; ++ ++ if (emptinessMap == null) { ++ return 15; ++ } ++ ++ // are we above this chunk's lowest empty section? ++ int lowestY = -2; ++ for (int currY = 15; currY >= 0; --currY) { ++ if (emptinessMap[currY]) { ++ continue; ++ } ++ ++ // should always be full lit here ++ lowestY = currY; ++ break; ++ } ++ ++ if (sectionY > lowestY) { ++ return 15; ++ } ++ ++ // this nibble is going to depend solely on the skylight data above it ++ // find first non-null data above (there does exist one, as we just found it above) ++ for (int currY = sectionY + 1; currY <= 16; ++currY) { ++ final SWMRNibbleArray nibble = nibbles[currY + 1]; ++ if (StarLightInterface.this.isClientSide) { ++ if (!nibble.isNullNibbleUpdating()) { ++ return nibble.getUpdating(x, 0, z); ++ } ++ } else { ++ if (!nibble.isNullNibbleVisible()) { ++ return nibble.getVisible(x, 0, z); ++ } ++ } ++ } ++ ++ // should never reach here ++ return 15; ++ } ++ ++ @Override ++ public void a(final SectionPosition pos, final boolean notReady) { ++ StarLightInterface.this.sectionChange(pos, notReady); ++ } ++ }; ++ this.blockReader = !hasBlockLight ? LightEngineLayerEventListener.Void.INSTANCE : new LightEngineLayerEventListener() { ++ @Override ++ public NibbleArray a(final SectionPosition pos) { ++ final IChunkAccess chunk = StarLightInterface.this.getAnyChunkNow(pos.getX(), pos.getZ()); ++ return chunk != null ? chunk.getBlockNibbles()[pos.getY() + 1].toVanillaNibble() : null; ++ } ++ ++ @Override ++ public int b(final BlockPosition blockPos) { ++ final int cx = blockPos.getX() >> 4; ++ final int cy = blockPos.getY() >> 4; ++ final int cz = blockPos.getZ() >> 4; ++ ++ if (cy < -1 || cy > 16) { ++ return 0; ++ } ++ ++ final IChunkAccess chunk = StarLightInterface.this.getAnyChunkNow(cx, cz); ++ ++ if (chunk == null) { ++ return 0; ++ } ++ ++ final SWMRNibbleArray nibble = chunk.getBlockNibbles()[cy + 1]; ++ if (StarLightInterface.this.isClientSide) { ++ return nibble.getUpdating(blockPos.getX(), blockPos.getY(), blockPos.getZ()); ++ } else { ++ return nibble.getVisible(blockPos.getX(), blockPos.getY(), blockPos.getZ()); ++ } ++ } ++ ++ @Override ++ public void a(final SectionPosition pos, final boolean notReady) { ++ return; // block engine doesn't care ++ } ++ }; ++ } ++ ++ public LightEngineLayerEventListener getSkyReader() { ++ return this.skyReader; ++ } ++ ++ public LightEngineLayerEventListener getBlockReader() { ++ return this.blockReader; ++ } ++ ++ public boolean isClientSide() { ++ return this.isClientSide; ++ } ++ ++ public IChunkAccess getAnyChunkNow(final int chunkX, final int chunkZ) { ++ if (this.world == null) { ++ // empty world ++ return null; ++ } ++ return this.world.getChunkProvider().getChunkAtImmediately(chunkX, chunkZ); ++ } ++ ++ public boolean hasUpdates() { ++ synchronized (this) { ++ return !this.changedBlocks.isEmpty(); ++ } + } + + public WorldServer getWorld() { + return this.world; + } + ++ public ILightAccess getLightAccess() { ++ return this.lightAccess; ++ } ++ + protected final SkyStarLightEngine getSkyLightEngine() { + if (this.cachedSkyPropagators == null) { + return null; @@ -3294,7 +4250,7 @@ index 0000000000000000000000000000000000000000..767831d169710388613396fcf8845453 + } + + if (ret == null) { -+ return new SkyStarLightEngine(); ++ return new SkyStarLightEngine(this.isClientSide); + } + return ret; + } @@ -3318,7 +4274,7 @@ index 0000000000000000000000000000000000000000..767831d169710388613396fcf8845453 + } + + if (ret == null) { -+ return new BlockStarLightEngine(); ++ return new BlockStarLightEngine(this.isClientSide); + } + return ret; + } @@ -3333,28 +4289,61 @@ index 0000000000000000000000000000000000000000..767831d169710388613396fcf8845453 + } + + public void blockChange(BlockPosition pos) { -+ if (pos.getY() < 0 || pos.getY() > 255) { ++ if (pos.getY() < 0 || pos.getY() > 255 || this.world == null) { // empty world + return; + } + + pos = pos.immutableCopy(); + synchronized (this.changedBlocks) { -+ this.changedBlocks.computeIfAbsent(MCUtil.getCoordinateKey(pos), (final long keyInMap) -> { -+ return new HashSet<>(); -+ }).add(pos); ++ this.changedBlocks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (final long keyInMap) -> { ++ return new ChunkChanges(); ++ }).changedPositions.add(pos); + } + } + -+ public void lightChunk(final int chunkX, final int chunkZ) { ++ public void sectionChange(final SectionPosition pos, final boolean newEmptyValue) { ++ if (this.world == null) { // empty world ++ return; ++ } ++ ++ synchronized (this.changedBlocks) { ++ final ChunkChanges changes = this.changedBlocks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (final long keyInMap) -> { ++ return new ChunkChanges(); ++ }); ++ if (changes.changedSectionSet == null) { ++ changes.changedSectionSet = new Boolean[16]; ++ } ++ changes.changedSectionSet[pos.getY()] = Boolean.valueOf(newEmptyValue); ++ } ++ } ++ ++ public void loadInChunk(final int chunkX, final int chunkZ, final Boolean[] emptySections) { + final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); + final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); + + try { + if (skyEngine != null) { -+ skyEngine.light(this.lightAccess, chunkX, chunkZ); ++ skyEngine.handleEmptySectionChanges(this.lightAccess, chunkX, chunkZ, emptySections); + } + if (blockEngine != null) { -+ blockEngine.light(this.lightAccess, chunkX, chunkZ); ++ blockEngine.handleEmptySectionChanges(this.lightAccess, chunkX, chunkZ, emptySections); ++ } ++ } finally { ++ this.releaseSkyLightEngine(skyEngine); ++ this.releaseBlockLightEngine(blockEngine); ++ } ++ } ++ ++ public void lightChunk(final int chunkX, final int chunkZ, final Boolean[] emptySections) { ++ final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ ++ try { ++ if (skyEngine != null) { ++ skyEngine.light(this.lightAccess, chunkX, chunkZ, emptySections); ++ } ++ if (blockEngine != null) { ++ blockEngine.light(this.lightAccess, chunkX, chunkZ, emptySections); + } + } finally { + this.releaseSkyLightEngine(skyEngine); @@ -3380,18 +4369,52 @@ index 0000000000000000000000000000000000000000..767831d169710388613396fcf8845453 + } + + public void checkChunkEdges(final int chunkX, final int chunkZ) { ++ this.checkSkyEdges(chunkX, chunkZ); ++ this.checkBlockEdges(chunkX, chunkZ); ++ } ++ ++ public void checkSkyEdges(final int chunkX, final int chunkZ) { + final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); -+ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); + + try { + if (skyEngine != null) { + skyEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ); + } ++ } finally { ++ this.releaseSkyLightEngine(skyEngine); ++ } ++ } ++ ++ public void checkBlockEdges(final int chunkX, final int chunkZ) { ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ try { + if (blockEngine != null) { + blockEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ); + } + } finally { ++ this.releaseBlockLightEngine(blockEngine); ++ } ++ } ++ ++ public void checkSkyEdges(final int chunkX, final int chunkZ, final ShortCollection sections) { ++ final SkyStarLightEngine skyEngine = this.getSkyLightEngine(); ++ ++ try { ++ if (skyEngine != null) { ++ skyEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, sections); ++ } ++ } finally { + this.releaseSkyLightEngine(skyEngine); ++ } ++ } ++ ++ public void checkBlockEdges(final int chunkX, final int chunkZ, final ShortCollection sections) { ++ final BlockStarLightEngine blockEngine = this.getBlockLightEngine(); ++ try { ++ if (blockEngine != null) { ++ blockEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, sections); ++ } ++ } finally { + this.releaseBlockLightEngine(blockEngine); + } + } @@ -3407,22 +4430,27 @@ index 0000000000000000000000000000000000000000..767831d169710388613396fcf8845453 + + try { + // TODO be smarter about this in the future -+ final Long2ObjectOpenHashMap> changedBlocks; ++ final Long2ObjectOpenHashMap changedBlocks; + synchronized (this.changedBlocks) { + changedBlocks = this.changedBlocks.clone(); + this.changedBlocks.clear(); + } + -+ for (final Iterator>> iterator = changedBlocks.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) { -+ final Long2ObjectMap.Entry> entry = iterator.next(); ++ for (final Iterator> iterator = changedBlocks.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) { ++ final Long2ObjectMap.Entry entry = iterator.next(); + final long coordinate = entry.getLongKey(); -+ final Set positions = entry.getValue(); ++ final ChunkChanges changes = entry.getValue(); ++ final Set positions = changes.changedPositions; ++ final Boolean[] sectionChanges = changes.changedSectionSet; ++ ++ final int chunkX = CoordinateUtils.getChunkX(coordinate); ++ final int chunkZ = CoordinateUtils.getChunkZ(coordinate); + + if (skyEngine != null) { -+ skyEngine.blocksChangedInChunk(this.lightAccess, MCUtil.getCoordinateX(coordinate), MCUtil.getCoordinateZ(coordinate), positions); ++ skyEngine.blocksChangedInChunk(this.lightAccess, chunkX, chunkZ, positions, sectionChanges); + } + if (blockEngine != null) { -+ blockEngine.blocksChangedInChunk(this.lightAccess, MCUtil.getCoordinateX(coordinate), MCUtil.getCoordinateZ(coordinate), positions); ++ blockEngine.blocksChangedInChunk(this.lightAccess, chunkX, chunkZ, positions, sectionChanges); + } + } + } finally { @@ -3430,6 +4458,17 @@ index 0000000000000000000000000000000000000000..767831d169710388613396fcf8845453 + this.releaseBlockLightEngine(blockEngine); + } + } ++ ++ protected static final class ChunkChanges { ++ ++ // note: on the main thread, empty section changes are queued before block changes. This means we don't need ++ // to worry about cases where a block change is called inside an empty chunk section, according to the "emptiness" map per chunk, ++ // for example. ++ public final Set changedPositions = new HashSet<>(); ++ ++ public Boolean[] changedSectionSet; ++ ++ } +} diff --git a/src/main/java/com/tuinity/tuinity/config/TuinityConfig.java b/src/main/java/com/tuinity/tuinity/config/TuinityConfig.java new file mode 100644 @@ -3899,6 +4938,366 @@ index 0000000000000000000000000000000000000000..21e50c75e0bffaa5cc5faf6aa81ae742 + TEMP_GET_CHUNKS_LIST.completeReset(); + } +} +diff --git a/src/main/java/com/tuinity/tuinity/util/CoordinateUtils.java b/src/main/java/com/tuinity/tuinity/util/CoordinateUtils.java +new file mode 100644 +index 0000000000000000000000000000000000000000..224c79e015fc7a87ef8fc357d56f9253576f4c67 +--- /dev/null ++++ b/src/main/java/com/tuinity/tuinity/util/CoordinateUtils.java +@@ -0,0 +1,122 @@ ++package com.tuinity.tuinity.util; ++ ++import net.minecraft.server.BlockPosition; ++import net.minecraft.server.ChunkCoordIntPair; ++import net.minecraft.server.Entity; ++import net.minecraft.server.MathHelper; ++import net.minecraft.server.SectionPosition; ++ ++public final class CoordinateUtils { ++ ++ // the chunk keys are compatible with vanilla ++ ++ public static long getChunkKey(final BlockPosition pos) { ++ return ((long)(pos.getZ() >> 4) << 32) | ((pos.getX() >> 4) & 0xFFFFFFFFL); ++ } ++ ++ public static long getChunkKey(final Entity entity) { ++ return ((long)(MathHelper.floor(entity.locZ()) >> 4) << 32) | ((MathHelper.floor(entity.locX()) >> 4) & 0xFFFFFFFFL); ++ } ++ ++ public static long getChunkKey(final ChunkCoordIntPair pos) { ++ return ((long)pos.z << 32) | (pos.x & 0xFFFFFFFFL); ++ } ++ ++ public static long getChunkKey(final SectionPosition pos) { ++ return ((long)pos.getZ() << 32) | (pos.getX() & 0xFFFFFFFFL); ++ } ++ ++ public static long getChunkKey(final int x, final int z) { ++ return ((long)z << 32) | (x & 0xFFFFFFFFL); ++ } ++ ++ public static int getChunkX(final long chunkKey) { ++ return (int)chunkKey; ++ } ++ ++ public static int getChunkZ(final long chunkKey) { ++ return (int)(chunkKey >>> 32); ++ } ++ ++ public static int getChunkCoordinate(final double blockCoordinate) { ++ return MathHelper.floor(blockCoordinate) >> 4; ++ } ++ ++ // the section keys are compatible with vanilla's ++ ++ static final int SECTION_X_BITS = 22; ++ static final long SECTION_X_MASK = (1L << SECTION_X_BITS) - 1; ++ static final int SECTION_Y_BITS = 20; ++ static final long SECTION_Y_MASK = (1L << SECTION_Y_BITS) - 1; ++ static final int SECTION_Z_BITS = 22; ++ static final long SECTION_Z_MASK = (1L << SECTION_Z_BITS) - 1; ++ // format is y,z,x (in order of LSB to MSB) ++ static final int SECTION_Y_SHIFT = 0; ++ static final int SECTION_Z_SHIFT = SECTION_Y_SHIFT + SECTION_Y_BITS; ++ static final int SECTION_X_SHIFT = SECTION_Z_SHIFT + SECTION_X_BITS; ++ static final int SECTION_TO_BLOCK_SHIFT = 4; ++ ++ public static long getChunkSectionKey(final int x, final int y, final int z) { ++ return ((x & SECTION_X_MASK) << SECTION_X_SHIFT) ++ | ((y & SECTION_Y_MASK) << SECTION_Y_SHIFT) ++ | ((z & SECTION_Z_MASK) << SECTION_Z_SHIFT); ++ } ++ ++ public static long getChunkSectionKey(final SectionPosition pos) { ++ return ((pos.getX() & SECTION_X_MASK) << SECTION_X_SHIFT) ++ | ((pos.getY() & SECTION_Y_MASK) << SECTION_Y_SHIFT) ++ | ((pos.getZ() & SECTION_Z_MASK) << SECTION_Z_SHIFT); ++ } ++ ++ public static long getChunkSectionKey(final ChunkCoordIntPair pos, final int y) { ++ return ((pos.x & SECTION_X_MASK) << SECTION_X_SHIFT) ++ | ((y & SECTION_Y_MASK) << SECTION_Y_SHIFT) ++ | ((pos.z & SECTION_Z_MASK) << SECTION_Z_SHIFT); ++ } ++ ++ public static long getChunkSectionKey(final BlockPosition pos) { ++ return (((long)pos.getX() << (SECTION_X_SHIFT - SECTION_TO_BLOCK_SHIFT)) & (SECTION_X_MASK << SECTION_X_SHIFT)) | ++ ((pos.getY() >> SECTION_TO_BLOCK_SHIFT) & (SECTION_Y_MASK << SECTION_Y_SHIFT)) | ++ (((long)pos.getZ() << (SECTION_Z_SHIFT - SECTION_TO_BLOCK_SHIFT)) & (SECTION_Z_MASK << SECTION_Z_SHIFT)); ++ } ++ ++ public static long getChunkSectionKey(final Entity entity) { ++ return ((MathHelper.floorLong(entity.locX()) << (SECTION_X_SHIFT - SECTION_TO_BLOCK_SHIFT)) & (SECTION_X_MASK << SECTION_X_SHIFT)) | ++ ((MathHelper.floorLong(entity.locY()) >> SECTION_TO_BLOCK_SHIFT) & (SECTION_Y_MASK << SECTION_Y_SHIFT)) | ++ ((MathHelper.floorLong(entity.locZ()) << (SECTION_Z_SHIFT - SECTION_TO_BLOCK_SHIFT)) & (SECTION_Z_MASK << SECTION_Z_SHIFT)); ++ } ++ ++ public static int getChunkSectionX(final long key) { ++ return (int)(key << (Long.SIZE - (SECTION_X_SHIFT + SECTION_X_BITS)) >> (Long.SIZE - SECTION_X_BITS)); ++ } ++ ++ public static int getChunkSectionY(final long key) { ++ return (int)(key << (Long.SIZE - (SECTION_Y_SHIFT + SECTION_Y_BITS)) >> (Long.SIZE - SECTION_Y_BITS)); ++ } ++ ++ public static int getChunkSectionZ(final long key) { ++ return (int)(key << (Long.SIZE - (SECTION_Z_SHIFT + SECTION_Z_BITS)) >> (Long.SIZE - SECTION_Z_BITS)); ++ } ++ ++ // the block coordinates are not necessarily compatible with vanilla's ++ ++ public static int getBlockCoordinate(final double blockCoordinate) { ++ return MathHelper.floor(blockCoordinate); ++ } ++ ++ public static long getBlockKey(final int x, final int y, final int z) { ++ return ((long)x & 0x7FFFFFF) | (((long)z & 0x7FFFFFF) << 27) | ((long)y << 54); ++ } ++ ++ public static long getBlockKey(final BlockPosition pos) { ++ return ((long)pos.getX() & 0x7FFFFFF) | (((long)pos.getZ() & 0x7FFFFFF) << 27) | ((long)pos.getY() << 54); ++ } ++ ++ public static long getBlockKey(final Entity entity) { ++ return ((long)entity.locX() & 0x7FFFFFF) | (((long)entity.locZ() & 0x7FFFFFF) << 27) | ((long)entity.locY() << 54); ++ } ++ ++ private CoordinateUtils() { ++ throw new RuntimeException(); ++ } ++} +diff --git a/src/main/java/com/tuinity/tuinity/util/IntegerUtil.java b/src/main/java/com/tuinity/tuinity/util/IntegerUtil.java +new file mode 100644 +index 0000000000000000000000000000000000000000..695444a510e616180734f5fd284f1a00a2d73ea6 +--- /dev/null ++++ b/src/main/java/com/tuinity/tuinity/util/IntegerUtil.java +@@ -0,0 +1,226 @@ ++package com.tuinity.tuinity.util; ++ ++public final class IntegerUtil { ++ ++ public static final int HIGH_BIT_U32 = Integer.MIN_VALUE; ++ public static final long HIGH_BIT_U64 = Long.MIN_VALUE; ++ ++ public static int ceilLog2(final int value) { ++ return Integer.SIZE - Integer.numberOfLeadingZeros(value - 1); // see doc of numberOfLeadingZeros ++ } ++ ++ public static long ceilLog2(final long value) { ++ return Long.SIZE - Long.numberOfLeadingZeros(value - 1); // see doc of numberOfLeadingZeros ++ } ++ ++ public static int floorLog2(final int value) { ++ // xor is optimized subtract for 2^n -1 ++ // note that (2^n -1) - k = (2^n -1) ^ k for k <= (2^n - 1) ++ return (Integer.SIZE - 1) ^ Integer.numberOfLeadingZeros(value); // see doc of numberOfLeadingZeros ++ } ++ ++ public static int floorLog2(final long value) { ++ // xor is optimized subtract for 2^n -1 ++ // note that (2^n -1) - k = (2^n -1) ^ k for k <= (2^n - 1) ++ return (Long.SIZE - 1) ^ Long.numberOfLeadingZeros(value); // see doc of numberOfLeadingZeros ++ } ++ ++ public static int roundCeilLog2(final int value) { ++ // optimized variant of 1 << (32 - leading(val - 1)) ++ // given ++ // 1 << n = HIGH_BIT_32 >>> (31 - n) for n [0, 32) ++ // 1 << (32 - leading(val - 1)) = HIGH_BIT_32 >>> (31 - (32 - leading(val - 1))) ++ // HIGH_BIT_32 >>> (31 - (32 - leading(val - 1))) ++ // HIGH_BIT_32 >>> (31 - 32 + leading(val - 1)) ++ // HIGH_BIT_32 >>> (-1 + leading(val - 1)) ++ return HIGH_BIT_U32 >>> (Integer.numberOfLeadingZeros(value - 1) - 1); ++ } ++ ++ public static long roundCeilLog2(final long value) { ++ // see logic documented above ++ return HIGH_BIT_U64 >>> (Long.numberOfLeadingZeros(value - 1) - 1); ++ } ++ ++ public static int roundFloorLog2(final int value) { ++ // optimized variant of 1 << (31 - leading(val)) ++ // given ++ // 1 << n = HIGH_BIT_32 >>> (31 - n) for n [0, 32) ++ // 1 << (31 - leading(val)) = HIGH_BIT_32 >> (31 - (31 - leading(val))) ++ // HIGH_BIT_32 >> (31 - (31 - leading(val))) ++ // HIGH_BIT_32 >> (31 - 31 + leading(val)) ++ return HIGH_BIT_U32 >>> Integer.numberOfLeadingZeros(value); ++ } ++ ++ public static long roundFloorLog2(final long value) { ++ // see logic documented above ++ return HIGH_BIT_U64 >>> Long.numberOfLeadingZeros(value); ++ } ++ ++ public static boolean isPowerOfTwo(final int n) { ++ // 2^n has one bit ++ // note: this rets true for 0 still ++ return IntegerUtil.getTrailingBit(n) == n; ++ } ++ ++ public static boolean isPowerOfTwo(final long n) { ++ // 2^n has one bit ++ // note: this rets true for 0 still ++ return IntegerUtil.getTrailingBit(n) == n; ++ } ++ ++ public static int getTrailingBit(final int n) { ++ return -n & n; ++ } ++ ++ public static long getTrailingBit(final long n) { ++ return -n & n; ++ } ++ ++ public static int trailingZeros(final int n) { ++ return Integer.numberOfTrailingZeros(n); ++ } ++ ++ public static int trailingZeros(final long n) { ++ return Long.numberOfTrailingZeros(n); ++ } ++ ++ // from hacker's delight (signed division magic value) ++ public static int getDivisorMultiple(final long numbers) { ++ return (int)(numbers >>> 32); ++ } ++ ++ // from hacker's delight (signed division magic value) ++ public static int getDivisorShift(final long numbers) { ++ return (int)numbers; ++ } ++ ++ // copied from hacker's delight (signed division magic value) ++ // http://www.hackersdelight.org/hdcodetxt/magic.c.txt ++ public static long getDivisorNumbers(final int d) { ++ final int ad = IntegerUtil.branchlessAbs(d); ++ ++ if (ad < 2) { ++ throw new IllegalArgumentException("|number| must be in [2, 2^31 -1], not: " + d); ++ } ++ ++ final int two31 = 0x80000000; ++ final long mask = 0xFFFFFFFFL; // mask for enforcing unsigned behaviour ++ ++ int p = 31; ++ ++ // all these variables are UNSIGNED! ++ int t = two31 + (d >>> 31); ++ int anc = t - 1 - t%ad; ++ int q1 = (int)((two31 & mask)/(anc & mask)); ++ int r1 = two31 - q1*anc; ++ int q2 = (int)((two31 & mask)/(ad & mask)); ++ int r2 = two31 - q2*ad; ++ int delta; ++ ++ do { ++ p = p + 1; ++ q1 = 2*q1; // Update q1 = 2**p/|nc|. ++ r1 = 2*r1; // Update r1 = rem(2**p, |nc|). ++ if ((r1 & mask) >= (anc & mask)) {// (Must be an unsigned comparison here) ++ q1 = q1 + 1; ++ r1 = r1 - anc; ++ } ++ q2 = 2*q2; // Update q2 = 2**p/|d|. ++ r2 = 2*r2; // Update r2 = rem(2**p, |d|). ++ if ((r2 & mask) >= (ad & mask)) {// (Must be an unsigned comparison here) ++ q2 = q2 + 1; ++ r2 = r2 - ad; ++ } ++ delta = ad - r2; ++ } while ((q1 & mask) < (delta & mask) || (q1 == delta && r1 == 0)); ++ ++ int magicNum = q2 + 1; ++ if (d < 0) { ++ magicNum = -magicNum; ++ } ++ int shift = p - 32; ++ return ((long)magicNum << 32) | shift; ++ } ++ ++ public static int branchlessAbs(final int val) { ++ // -n = -1 ^ n + 1 ++ final int mask = val >> (Integer.SIZE - 1); // -1 if < 0, 0 if >= 0 ++ return (mask ^ val) - mask; // if val < 0, then (0 ^ val) - 0 else (-1 ^ val) + 1 ++ } ++ ++ public static long branchlessAbs(final long val) { ++ // -n = -1 ^ n + 1 ++ final long mask = val >> (Long.SIZE - 1); // -1 if < 0, 0 if >= 0 ++ return (mask ^ val) - mask; // if val < 0, then (0 ^ val) - 0 else (-1 ^ val) + 1 ++ } ++ ++ //https://github.com/skeeto/hash-prospector for hash functions ++ ++ //score = ~590.47984224483832 ++ public static int hash0(int x) { ++ x *= 0x36935555; ++ x ^= x >>> 16; ++ return x; ++ } ++ ++ //score = ~310.01596637036749 ++ public static int hash1(int x) { ++ x ^= x >>> 15; ++ x *= 0x356aaaad; ++ x ^= x >>> 17; ++ return x; ++ } ++ ++ public static int hash2(int x) { ++ x ^= x >>> 16; ++ x *= 0x7feb352d; ++ x ^= x >>> 15; ++ x *= 0x846ca68b; ++ x ^= x >>> 16; ++ return x; ++ } ++ ++ public static int hash3(int x) { ++ x ^= x >>> 17; ++ x *= 0xed5ad4bb; ++ x ^= x >>> 11; ++ x *= 0xac4c1b51; ++ x ^= x >>> 15; ++ x *= 0x31848bab; ++ x ^= x >>> 14; ++ return x; ++ } ++ ++ //score = ~365.79959673201887 ++ public static long hash1(long x) { ++ x ^= x >>> 27; ++ x *= 0xb24924b71d2d354bL; ++ x ^= x >>> 28; ++ return x; ++ } ++ ++ //h2 hash ++ public static long hash2(long x) { ++ x ^= x >>> 32; ++ x *= 0xd6e8feb86659fd93L; ++ x ^= x >>> 32; ++ x *= 0xd6e8feb86659fd93L; ++ x ^= x >>> 32; ++ return x; ++ } ++ ++ public static long hash3(long x) { ++ x ^= x >>> 45; ++ x *= 0xc161abe5704b6c79L; ++ x ^= x >>> 41; ++ x *= 0xe3e5389aedbc90f7L; ++ x ^= x >>> 56; ++ x *= 0x1f9aba75a52db073L; ++ x ^= x >>> 53; ++ return x; ++ } ++ ++ private IntegerUtil() { ++ throw new RuntimeException(); ++ } ++} diff --git a/src/main/java/com/tuinity/tuinity/util/IntervalledCounter.java b/src/main/java/com/tuinity/tuinity/util/IntervalledCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..d2c7d2c7920324d7207225ed19484e804368489d @@ -5586,10 +6985,10 @@ index 0000000000000000000000000000000000000000..002abb3cbf0f742e685f2f043d2600de + } +} diff --git a/src/main/java/net/minecraft/server/AxisAlignedBB.java b/src/main/java/net/minecraft/server/AxisAlignedBB.java -index ed9b2f9adfecdc6d1b9925579ec510657adde11f..9fbb77e70d1abaf393fd6e4fd5cb124fcaedaef8 100644 +index ed9b2f9adfecdc6d1b9925579ec510657adde11f..fd3bb6dfa6cc060e9785c22a9e61a4325c348e36 100644 --- a/src/main/java/net/minecraft/server/AxisAlignedBB.java +++ b/src/main/java/net/minecraft/server/AxisAlignedBB.java -@@ -13,6 +13,149 @@ public class AxisAlignedBB { +@@ -13,6 +13,157 @@ public class AxisAlignedBB { public final double maxY; public final double maxZ; @@ -5731,6 +7130,14 @@ index ed9b2f9adfecdc6d1b9925579ec510657adde11f..9fbb77e70d1abaf393fd6e4fd5cb124f + return new AxisAlignedBB(this.minX, this.minY, this.minZ, this.maxX, this.maxY + dy, this.maxZ, false); + } + ++ public final AxisAlignedBB cutUpwards(final double dy) { // dy > 0.0 ++ return new AxisAlignedBB(this.minX, this.maxY, this.minZ, this.maxX, this.maxY + dy, this.maxZ, false); ++ } ++ ++ public final AxisAlignedBB cutDownwards(final double dy) { // dy < 0.0 ++ return new AxisAlignedBB(this.minX, this.minY + dy, this.minZ, this.maxX, this.minY, this.maxZ, false); ++ } ++ + public final AxisAlignedBB expandUpwardsAndCutBelow(double dy) { + return new AxisAlignedBB(this.minX, this.maxY, this.minZ, this.maxX, this.maxY + dy, this.maxZ, false); + } @@ -5739,7 +7146,7 @@ index ed9b2f9adfecdc6d1b9925579ec510657adde11f..9fbb77e70d1abaf393fd6e4fd5cb124f public AxisAlignedBB(double d0, double d1, double d2, double d3, double d4, double d5) { this.minX = Math.min(d0, d3); this.minY = Math.min(d1, d4); -@@ -185,6 +328,7 @@ public class AxisAlignedBB { +@@ -185,6 +336,7 @@ public class AxisAlignedBB { return new AxisAlignedBB(d0, d1, d2, d3, d4, d5); } @@ -5747,7 +7154,7 @@ index ed9b2f9adfecdc6d1b9925579ec510657adde11f..9fbb77e70d1abaf393fd6e4fd5cb124f public AxisAlignedBB d(double d0, double d1, double d2) { return new AxisAlignedBB(this.minX + d0, this.minY + d1, this.minZ + d2, this.maxX + d0, this.maxY + d1, this.maxZ + d2); } -@@ -193,6 +337,7 @@ public class AxisAlignedBB { +@@ -193,6 +345,7 @@ public class AxisAlignedBB { return new AxisAlignedBB(this.minX + (double) blockposition.getX(), this.minY + (double) blockposition.getY(), this.minZ + (double) blockposition.getZ(), this.maxX + (double) blockposition.getX(), this.maxY + (double) blockposition.getY(), this.maxZ + (double) blockposition.getZ()); } @@ -5755,7 +7162,7 @@ index ed9b2f9adfecdc6d1b9925579ec510657adde11f..9fbb77e70d1abaf393fd6e4fd5cb124f public AxisAlignedBB c(Vec3D vec3d) { return this.d(vec3d.x, vec3d.y, vec3d.z); } -@@ -212,6 +357,7 @@ public class AxisAlignedBB { +@@ -212,6 +365,7 @@ public class AxisAlignedBB { return this.e(vec3d.x, vec3d.y, vec3d.z); } @@ -6250,7 +7657,7 @@ index a33303c31881b6391723e16a06d7841d48679958..ce57e6a4acac97d6da82202094306e7e return this.b.equals(entityliving.getEntityType()) && this.d.test(entityliving); } diff --git a/src/main/java/net/minecraft/server/BlockBase.java b/src/main/java/net/minecraft/server/BlockBase.java -index 1f334d63282bd5c23dc3b275a220f09e60c34537..4d1ac4e6b61897fc03b091475ef7be3ed0b228a9 100644 +index 1f334d63282bd5c23dc3b275a220f09e60c34537..829d4a7508e1656dbdc912096b7eafcf30cbb5b2 100644 --- a/src/main/java/net/minecraft/server/BlockBase.java +++ b/src/main/java/net/minecraft/server/BlockBase.java @@ -295,21 +295,23 @@ public abstract class BlockBase { @@ -6289,7 +7696,7 @@ index 1f334d63282bd5c23dc3b275a220f09e60c34537..4d1ac4e6b61897fc03b091475ef7be3e } // Paper start - impl cached craft block data, lazy load to fix issue with loading at the wrong time private org.bukkit.craftbukkit.block.data.CraftBlockData cachedCraftBlockData; -@@ -343,12 +346,100 @@ public abstract class BlockBase { +@@ -343,12 +346,62 @@ public abstract class BlockBase { protected Fluid fluid; // Paper end @@ -6312,17 +7719,6 @@ index 1f334d63282bd5c23dc3b275a220f09e60c34537..4d1ac4e6b61897fc03b091475ef7be3e + return this.conditionallyFullOpaque; + } + // Tuinity end -+ -+ // Tuinity start -+ public static boolean isSpecialCollidingBlock(BlockBase.BlockData blockData) { -+ return blockData.shapeExceedsCube() || blockData.getBlock() == Blocks.MOVING_PISTON; -+ } -+ -+ protected long blockCollisionBehavior = ChunkSection.KNOWN_SPECIAL_BLOCK; -+ public final long getBlockCollisionBehavior() { -+ return this.blockCollisionBehavior; -+ } -+ // Tuinity end + public void a() { this.fluid = this.getBlock().d(this.p()); // Paper - moved from getFluid() @@ -6360,37 +7756,10 @@ index 1f334d63282bd5c23dc3b275a220f09e60c34537..4d1ac4e6b61897fc03b091475ef7be3e + } + } + // Tuinity end - optimise culling shape cache for light -+ // Tuinity start - init block collision behavior field -+ if (isSpecialCollidingBlock(this)) { -+ this.blockCollisionBehavior = ChunkSection.KNOWN_SPECIAL_BLOCK; -+ } else { -+ try { -+ VoxelShape constantShape = this.getCollisionShape(null, null, null); -+ if (constantShape == null) { -+ this.blockCollisionBehavior = ChunkSection.KNOWN_UNKNOWN_BLOCK; -+ } else { -+ if (constantShape.isEmpty()) { -+ this.blockCollisionBehavior = ChunkSection.KNOWN_EMPTY_BLOCK; -+ } else { -+ List boxes = constantShape.simplify().getBoundingBoxesRepresentation(); -+ if (boxes.size() == 1 && boxes.get(0).equals(VoxelShapes.optimisedFullCube.aabb)) { -+ this.blockCollisionBehavior = ChunkSection.KNOWN_FULL_BLOCK; -+ } else { -+ this.blockCollisionBehavior = ChunkSection.KNOWN_UNKNOWN_BLOCK; -+ } -+ } -+ } -+ } catch (ThreadDeath thr) { -+ throw thr; -+ } catch (Throwable thr) { -+ this.blockCollisionBehavior = ChunkSection.KNOWN_UNKNOWN_BLOCK; -+ } -+ } -+ // Tuinity end - init block collision behavior field } -@@ -372,10 +463,12 @@ public abstract class BlockBase { +@@ -372,10 +425,12 @@ public abstract class BlockBase { return this.a != null ? this.a.g : this.getBlock().b(this.p(), iblockaccess, blockposition); } @@ -6403,7 +7772,7 @@ index 1f334d63282bd5c23dc3b275a220f09e60c34537..4d1ac4e6b61897fc03b091475ef7be3e public VoxelShape a(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { return this.a != null && this.a.i != null ? this.a.i[enumdirection.ordinal()] : VoxelShapes.a(this.c(iblockaccess, blockposition), enumdirection); } -@@ -385,7 +478,7 @@ public abstract class BlockBase { +@@ -385,7 +440,7 @@ public abstract class BlockBase { } public final boolean d() { // Paper @@ -6412,7 +7781,7 @@ index 1f334d63282bd5c23dc3b275a220f09e60c34537..4d1ac4e6b61897fc03b091475ef7be3e } public final boolean e() { // Paper -@@ -675,9 +768,9 @@ public abstract class BlockBase { +@@ -675,9 +730,9 @@ public abstract class BlockBase { private static final int f = EnumBlockSupport.values().length; protected final boolean a; private final boolean g; @@ -6535,10 +7904,10 @@ index 2d887af902a33b0e28d8f0a6ac2e59c815a7856e..2291135eaef64c403183724cb6e413cd @Override public BlockPosition immutableCopy() { diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee65816e53 100644 +index 9c078d30afef20bd1ea5975299c5513334829b19..0d3fd9b9bebdecabb78f6e04aed18b6ede654456 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java -@@ -91,6 +91,175 @@ public class Chunk implements IChunkAccess { +@@ -91,6 +91,182 @@ public class Chunk implements IChunkAccess { private final int[] inventoryEntityCounts = new int[16]; // Paper end @@ -6592,8 +7961,9 @@ index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee + } + // Tuinity end - optimise hard collision handling + // Tuinity start - rewrite light engine -+ private volatile com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] blockNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(false); -+ private volatile com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] skyNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(true); ++ private volatile com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] blockNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(); ++ private volatile com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] skyNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(); ++ private volatile boolean[][] emptinessMap = new boolean[9][]; + + @Override + public com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] getBlockNibbles() { @@ -6614,6 +7984,12 @@ index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee + public void setSkyNibbles(com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] nibbles) { + this.skyNibbles = nibbles; + } ++ ++ @Override ++ public boolean[][] getEmptinessMap() { ++ return this.emptinessMap; ++ } ++ + // Tuinity end - rewrite light engine + + // Tuinity start - entity slices by class @@ -6714,18 +8090,19 @@ index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee public Chunk(World world, ChunkCoordIntPair chunkcoordintpair, BiomeStorage biomestorage, ChunkConverter chunkconverter, TickList ticklist, TickList ticklist1, long i, @Nullable ChunkSection[] achunksection, @Nullable Consumer consumer) { this.sections = new ChunkSection[16]; this.e = Maps.newHashMap(); -@@ -297,6 +466,10 @@ public class Chunk implements IChunkAccess { +@@ -297,6 +473,11 @@ public class Chunk implements IChunkAccess { public Chunk(World world, ProtoChunk protochunk) { this(world, protochunk.getPos(), protochunk.getBiomeIndex(), protochunk.p(), protochunk.n(), protochunk.o(), protochunk.getInhabitedTime(), protochunk.getSections(), (Consumer) null); + // Tuinity start - copy over protochunk light + this.blockNibbles = protochunk.getBlockNibbles(); + this.skyNibbles = protochunk.getSkyNibbles(); ++ this.emptinessMap = protochunk.getEmptinessMap(); + // Tuinity end - copy over protochunk light Iterator iterator = protochunk.y().iterator(); while (iterator.hasNext()) { -@@ -330,7 +503,7 @@ public class Chunk implements IChunkAccess { +@@ -330,7 +511,7 @@ public class Chunk implements IChunkAccess { Entry entry = (Entry) iterator.next(); if (ChunkStatus.FULL.h().contains(entry.getKey())) { @@ -6734,7 +8111,7 @@ index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee } } -@@ -547,6 +720,7 @@ public class Chunk implements IChunkAccess { +@@ -547,6 +728,7 @@ public class Chunk implements IChunkAccess { @Override public void a(Entity entity) { @@ -6742,7 +8119,7 @@ index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee this.q = true; int i = MathHelper.floor(entity.locX() / 16.0D); int j = MathHelper.floor(entity.locZ() / 16.0D); -@@ -592,8 +766,8 @@ public class Chunk implements IChunkAccess { +@@ -592,8 +774,8 @@ public class Chunk implements IChunkAccess { entity.chunkX = this.loc.x; entity.chunkY = k; entity.chunkZ = this.loc.z; @@ -6753,7 +8130,7 @@ index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee // Paper start if (entity instanceof EntityItem) { itemCounts[k]++; -@@ -616,6 +790,7 @@ public class Chunk implements IChunkAccess { +@@ -616,6 +798,7 @@ public class Chunk implements IChunkAccess { } public void a(Entity entity, int i) { @@ -6761,7 +8138,7 @@ index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee if (i < 0) { i = 0; } -@@ -630,7 +805,7 @@ public class Chunk implements IChunkAccess { +@@ -630,7 +813,7 @@ public class Chunk implements IChunkAccess { entity.entitySlice = null; entity.inChunk = false; } @@ -6770,7 +8147,7 @@ index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee return; } if (entity instanceof EntityItem) { -@@ -943,6 +1118,7 @@ public class Chunk implements IChunkAccess { +@@ -943,6 +1126,7 @@ public class Chunk implements IChunkAccess { } @@ -6779,7 +8156,7 @@ index 9c078d30afef20bd1ea5975299c5513334829b19..e5169c5c7a71566c22c5b3c4686e56ee org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot int i = MathHelper.floor((axisalignedbb.minY - 2.0D) / 16.0D); diff --git a/src/main/java/net/minecraft/server/ChunkCache.java b/src/main/java/net/minecraft/server/ChunkCache.java -index 8eecdcde510661ec3a13a25a04ba394f6b6dc012..187a639347413a3aef1b6025926e2bf5936c11fd 100644 +index 8eecdcde510661ec3a13a25a04ba394f6b6dc012..ab1085091fefea3a3fa15f7028bec050d00a6f5e 100644 --- a/src/main/java/net/minecraft/server/ChunkCache.java +++ b/src/main/java/net/minecraft/server/ChunkCache.java @@ -1,5 +1,6 @@ @@ -6789,7 +8166,7 @@ index 8eecdcde510661ec3a13a25a04ba394f6b6dc012..187a639347413a3aef1b6025926e2bf5 import java.util.function.Predicate; import java.util.stream.Stream; import javax.annotation.Nullable; -@@ -12,6 +13,216 @@ public class ChunkCache implements IBlockAccess, ICollisionAccess { +@@ -12,6 +13,156 @@ public class ChunkCache implements IBlockAccess, ICollisionAccess { protected boolean d; protected final World e; protected final World getWorld() { return e; } // Paper - OBFHELPER @@ -6893,103 +8270,43 @@ index 8eecdcde510661ec3a13a25a04ba394f6b6dc012..187a639347413a3aef1b6025926e2bf5 + continue; + } + -+ int minXIterate; -+ int maxXIterate; -+ int minZIterate; -+ int maxZIterate; -+ -+ boolean sectionHasSpecial = section.hasSpecialCollidingBlocks(); -+ if (!sectionHasSpecial && currChunkX == minChunkX) { -+ minXIterate = minX + 1; -+ } else { -+ minXIterate = minX; -+ } -+ if (!sectionHasSpecial && currChunkX == maxChunkX) { -+ maxXIterate = maxX - 1; -+ } else { -+ maxXIterate = maxX; -+ } -+ -+ if (!sectionHasSpecial && currChunkZ == minChunkZ) { -+ minZIterate = minZ + 1; -+ } else { -+ minZIterate = minZ; -+ } -+ if (!sectionHasSpecial && currChunkZ == maxChunkZ) { -+ maxZIterate = maxZ - 1; -+ } else { -+ maxZIterate = maxZ; -+ } -+ + DataPaletteBlock blocks = section.blockIds; + -+ for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) { -+ block_search_loop: -+ for (int currX = minXIterate; currX <= maxXIterate; ++currX) { ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { + int localBlockIndex = (currX) | (currZ << 4) | ((currY & 15) << 8); -+ long blockInfo = section.getKnownBlockInfo(localBlockIndex); -+ switch ((int)blockInfo) { -+ case (int)ChunkSection.KNOWN_EMPTY_BLOCK: { -+ continue block_search_loop; ++ int blockX = currX | chunkXGlobalPos; ++ int blockY = currY; ++ int blockZ = currZ | chunkZGlobalPos; ++ ++ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) + ++ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) + ++ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0); ++ if (edgeCount == 3) { ++ continue; ++ } ++ ++ IBlockData blockData = blocks.rawGet(localBlockIndex); ++ ++ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) { ++ mutablePos.setValues(blockX, blockY, blockZ); ++ if (collisionShape == null) { ++ collisionShape = entity == null ? VoxelShapeCollision.a() : VoxelShapeCollision.a(entity); + } -+ case (int)ChunkSection.KNOWN_FULL_BLOCK: { -+ AxisAlignedBB box = new AxisAlignedBB( -+ currX | chunkXGlobalPos, currY, currZ | chunkZGlobalPos, -+ (currX | chunkXGlobalPos) + 1, currY + 1, (currZ | chunkZGlobalPos) + 1, -+ false -+ ); -+ if (predicate != null) { -+ if (!box.voxelShapeIntersect(axisalignedbb)) { -+ continue block_search_loop; ++ VoxelShape voxelshape2 = blockData.getCollisionShape(this, mutablePos, collisionShape); ++ if (voxelshape2 != VoxelShapes.getEmptyShape()) { ++ VoxelShape voxelshape3 = voxelshape2.offset((double)blockX, (double)blockY, (double)blockZ); ++ ++ if (predicate != null && !predicate.test(blockData, mutablePos)) { ++ continue; ++ } ++ ++ if (checkOnly) { ++ if (voxelshape3.intersects(axisalignedbb)) { ++ return true; + } -+ // fall through to get the block for the predicate + } else { -+ if (box.voxelShapeIntersect(axisalignedbb)) { -+ if (checkOnly) { -+ return true; -+ } else { -+ list.add(box); -+ ret = true; -+ } -+ } -+ continue block_search_loop; -+ } -+ } -+ default: { -+ int blockX = currX | chunkXGlobalPos; -+ int blockY = currY; -+ int blockZ = currZ | chunkZGlobalPos; -+ -+ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) + -+ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) + -+ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0); -+ if (edgeCount == 3 || (edgeCount != 0 && blockInfo != ChunkSection.KNOWN_SPECIAL_BLOCK)) { -+ continue block_search_loop; -+ } -+ -+ IBlockData blockData = blocks.rawGet(localBlockIndex); -+ -+ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) { -+ if (collisionShape == null) { -+ collisionShape = entity == null ? VoxelShapeCollision.a() : VoxelShapeCollision.a(entity); -+ } -+ mutablePos.setValues(blockX, blockY, blockZ); -+ VoxelShape voxelshape2 = blockData.getCollisionShape(this, mutablePos, collisionShape); -+ if (voxelshape2 != VoxelShapes.getEmptyShape()) { -+ VoxelShape voxelshape3 = voxelshape2.offset((double)blockX, (double)blockY, (double)blockZ); -+ -+ if (predicate != null && !predicate.test(blockData, mutablePos)) { -+ continue block_search_loop; -+ } -+ -+ if (checkOnly) { -+ if (voxelshape3.intersects(axisalignedbb)) { -+ return true; -+ } -+ } else { -+ ret |= VoxelShapes.addBoxesToIfIntersects(voxelshape3, axisalignedbb, list); -+ } -+ } ++ ret |= VoxelShapes.addBoxesToIfIntersects(voxelshape3, axisalignedbb, list); + } + } + } @@ -7190,7 +8507,7 @@ index 3c7b225edbe23dc1959002293a6f8b816287b5a8..f1c686810fb4e9c05df45d664c93af73 for (java.util.Iterator>>> iterator = this.tickets.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) { diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 6acb5f05a05c542f8257e205ef70987be2d29194..84429f12d0c6e0990b7cbb1b503b7868b3a02b14 100644 +index 6acb5f05a05c542f8257e205ef70987be2d29194..12d9b73ccc2f4406957932397746cac7902d650e 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -22,6 +22,12 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; // Paper @@ -7559,7 +8876,7 @@ index 6acb5f05a05c542f8257e205ef70987be2d29194..84429f12d0c6e0990b7cbb1b503b7868 } private void a(long i, Consumer consumer) { -@@ -1002,44 +1209,11 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -1002,51 +1209,18 @@ public class ChunkProviderServer extends IChunkProvider { ChunkProviderServer.this.world.getMethodProfiler().c("runTask"); super.executeTask(runnable); } @@ -7606,8 +8923,16 @@ index 6acb5f05a05c542f8257e205ef70987be2d29194..84429f12d0c6e0990b7cbb1b503b7868 // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task try { boolean execChunkTask = com.destroystokyo.paper.io.chunk.ChunkTaskManager.pollChunkWaitQueue() || ChunkProviderServer.this.world.asyncChunkTaskManager.pollNextChunkTask(); // Paper + if (ChunkProviderServer.this.tickDistanceManager()) { + return true; + } else { +- //ChunkProviderServer.this.lightEngine.queueUpdate(); // Paper - not needed ++ ChunkProviderServer.this.lightEngine.queueUpdate(); // Paper - not needed // Tuinity - prevent queue overflow when no players are in this world + return super.executeNext() || execChunkTask; // Paper + } + } finally { diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 8e7da2c5f3852920ec5fbcdd2bff4d299e6aa499..64dd95292fb4d058f6200bfcadaedfbd62b2461d 100644 +index 8e7da2c5f3852920ec5fbcdd2bff4d299e6aa499..1f1f1a0cb84a07087025fd831441d0571d6de7c6 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java @@ -24,6 +24,14 @@ public class ChunkRegionLoader { @@ -7625,61 +8950,62 @@ index 8e7da2c5f3852920ec5fbcdd2bff4d299e6aa499..64dd95292fb4d058f6200bfcadaedfbd // Paper start - guard against serializing mismatching coordinates // TODO Note: This needs to be re-checked each update public static ChunkCoordIntPair getChunkCoordinate(NBTTagCompound chunkData) { -@@ -85,13 +93,15 @@ public class ChunkRegionLoader { +@@ -56,6 +64,13 @@ public class ChunkRegionLoader { + private static final boolean JUST_CORRUPT_IT = Boolean.getBoolean("Paper.ignoreWorldDataVersion"); + // Paper end + ++ // Tuinity start - rewrite light engine ++ private static final int STARLIGHT_LIGHT_VERSION = 1; ++ ++ private static final String UNINITIALISED_SKYLIGHT_TAG = "starlight.skylight_uninit"; ++ private static final String STARLIGHT_VERSION_TAG = "starlight.light_version"; ++ // Tuinity end - rewrite light engine ++ + public static InProgressChunkHolder loadChunk(WorldServer worldserver, DefinedStructureManager definedstructuremanager, VillagePlace villageplace, ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound, boolean distinguish) { + ArrayDeque tasksToExecuteOnMain = new ArrayDeque<>(); + // Paper end +@@ -85,13 +100,15 @@ public class ChunkRegionLoader { ProtoChunkTickList protochunkticklist1 = new ProtoChunkTickList<>((fluidtype) -> { return fluidtype == null || fluidtype == FluidTypes.EMPTY; }, chunkcoordintpair, nbttagcompound1.getList("LiquidsToBeTicked", 9)); - boolean flag = nbttagcompound1.getBoolean("isLightOn"); -+ boolean flag = (!com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine || nbttagcompound1.getBoolean("starlight.lit")) && nbttagcompound1.getBoolean("isLightOn"); boolean canUseSkyLight = flag && getStatus(nbttagcompound).isAtLeastStatus(ChunkStatus.LIGHT); boolean canUseBlockLight = canUseSkyLight; // Tuinity ++ boolean flag = (com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? nbttagcompound1.getInt(STARLIGHT_VERSION_TAG) == STARLIGHT_LIGHT_VERSION : nbttagcompound1.getBoolean("isLightOn")); boolean canUseSkyLight = flag && getStatus(nbttagcompound).isAtLeastStatus(ChunkStatus.LIGHT); boolean canUseBlockLight = canUseSkyLight; // Tuinity NBTTagList nbttaglist = nbttagcompound1.getList("Sections", 10); boolean flag1 = true; ChunkSection[] achunksection = new ChunkSection[16]; boolean flag2 = worldserver.getDimensionManager().hasSkyLight(); ChunkProviderServer chunkproviderserver = worldserver.getChunkProvider(); LightEngine lightengine = chunkproviderserver.getLightEngine(); -+ com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] blockNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(false); // Tuinity - replace light impl -+ com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] skyNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(true); // Tuinity - replace light impl ++ com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] blockNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(); // Tuinity - replace light impl ++ com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] skyNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(); // Tuinity - replace light impl if (flag) { tasksToExecuteOnMain.add(() -> { // Paper - delay this task since we're executing off-main -@@ -119,6 +129,7 @@ public class ChunkRegionLoader { +@@ -119,6 +136,7 @@ public class ChunkRegionLoader { if (flag) { if (nbttagcompound2.hasKeyOfType("BlockLight", 7)) { -+ if (com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine && canUseBlockLight) blockNibbles[b0 + 1] = new com.tuinity.tuinity.chunk.light.SWMRNibbleArray(nbttagcompound2.getByteArray("BlockLight")); // Tuinity - replace light impl ++ if (com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine && canUseBlockLight) blockNibbles[b0 + 1] = new com.tuinity.tuinity.chunk.light.SWMRNibbleArray(nbttagcompound2.getByteArray("BlockLight").clone()); // Tuinity - replace light impl // Paper start - delay this task since we're executing off-main NibbleArray blockLight = new NibbleArray(nbttagcompound2.getByteArray("BlockLight")); tasksToExecuteOnMain.add(() -> { -@@ -128,6 +139,7 @@ public class ChunkRegionLoader { +@@ -128,13 +146,14 @@ public class ChunkRegionLoader { } if (flag2 && nbttagcompound2.hasKeyOfType("SkyLight", 7)) { -+ if (com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine && canUseSkyLight) skyNibbles[b0 + 1] = new com.tuinity.tuinity.chunk.light.SWMRNibbleArray(nbttagcompound2.getByteArray("SkyLight")); // Tuinity - replace light impl ++ if (com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine && canUseSkyLight) skyNibbles[b0 + 1] = new com.tuinity.tuinity.chunk.light.SWMRNibbleArray(nbttagcompound2.getByteArray("SkyLight").clone()); // Tuinity - replace light impl // Paper start - delay this task since we're executing off-main NibbleArray skyLight = new NibbleArray(nbttagcompound2.getByteArray("SkyLight")); tasksToExecuteOnMain.add(() -> { -@@ -138,6 +150,20 @@ public class ChunkRegionLoader { + lightengine.a(EnumSkyBlock.SKY, SectionPosition.a(chunkcoordintpair, b0), skyLight, true); + }); + // Paper end - delay this task since we're executing off-main +- } ++ } else if (flag2 && nbttagcompound2.getBoolean(UNINITIALISED_SKYLIGHT_TAG)) skyNibbles[b0 + 1] = new com.tuinity.tuinity.chunk.light.SWMRNibbleArray(); // Tuinity - replace light impl } } -+ // Tuinity start - correctly set nullable status for light data -+ boolean nullableSky = true; -+ for (int y = 16; y >= -1; --y) { -+ com.tuinity.tuinity.chunk.light.SWMRNibbleArray nibble = skyNibbles[y + 1]; -+ if (nibble.isInitialisedUpdating()) { -+ nullableSky = false; -+ continue; -+ } -+ if (!nullableSky) { -+ nibble.markNonNull(); -+ } -+ } -+ // Tuinity end - correctly set nullable status for light data -+ - long j = nbttagcompound1.getLong("InhabitedTime"); - ChunkStatus.Type chunkstatus_type = a(nbttagcompound); - Object object; -@@ -173,8 +199,12 @@ public class ChunkRegionLoader { +@@ -173,8 +192,12 @@ public class ChunkRegionLoader { object = new Chunk(worldserver.getMinecraftWorld(), chunkcoordintpair, biomestorage, chunkconverter, (TickList) object1, (TickList) object2, j, achunksection, // Paper start - fix massive nbt memory leak due to lambda. move lambda into a container method to not leak scope. Only clone needed NBT keys. createLoadEntitiesConsumer(new SafeNBTCopy(nbttagcompound1, "TileEntities", "Entities", "ChunkBukkitValues")) // Paper - move CB Chunk PDC into here );// Paper end @@ -7692,14 +9018,14 @@ index 8e7da2c5f3852920ec5fbcdd2bff4d299e6aa499..64dd95292fb4d058f6200bfcadaedfbd protochunk.a(biomestorage); object = protochunk; -@@ -354,14 +384,14 @@ public class ChunkRegionLoader { +@@ -354,14 +377,14 @@ public class ChunkRegionLoader { NibbleArray[] skyLight = new NibbleArray[17 - (-1)]; for (int i = -1; i < 17; ++i) { - NibbleArray blockArray = lightenginethreaded.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkPos, i)); - NibbleArray skyArray = lightenginethreaded.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkPos, i)); -+ NibbleArray blockArray = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? (!lightenginethreaded.hasBlockLight ? null : chunk.getBlockNibbles()[i + 1].asNibble()) : lightenginethreaded.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkPos, i)); // Tuinity - chunk might not be loaded -+ NibbleArray skyArray = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? (!lightenginethreaded.hasSkyLight ? null : chunk.getSkyNibbles()[i + 1].asNibble()) : lightenginethreaded.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkPos, i)); // Tuinity - chunk might not be loaded ++ NibbleArray blockArray = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? (!lightenginethreaded.hasBlockLight ? null : (chunk.getBlockNibbles()[i + 1].isAllZero() ? new NibbleArray() : chunk.getBlockNibbles()[i + 1].toVanillaNibble())) : lightenginethreaded.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkPos, i)); // Tuinity - chunk might not be loaded ++ NibbleArray skyArray = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? (!lightenginethreaded.hasSkyLight ? null : (chunk.getSkyNibbles()[i + 1].isAllZero() ? new NibbleArray() : chunk.getSkyNibbles()[i + 1].toVanillaNibble())) : lightenginethreaded.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkPos, i)); // Tuinity - chunk might not be loaded // copy data for safety - if (blockArray != null) { @@ -7711,7 +9037,7 @@ index 8e7da2c5f3852920ec5fbcdd2bff4d299e6aa499..64dd95292fb4d058f6200bfcadaedfbd skyArray = skyArray.copy(); } -@@ -401,10 +431,10 @@ public class ChunkRegionLoader { +@@ -401,10 +424,10 @@ public class ChunkRegionLoader { NBTTagCompound nbttagcompound1 = new NBTTagCompound(); nbttagcompound.setInt("DataVersion", SharedConstants.getGameVersion().getWorldVersion()); @@ -7724,18 +9050,18 @@ index 8e7da2c5f3852920ec5fbcdd2bff4d299e6aa499..64dd95292fb4d058f6200bfcadaedfbd nbttagcompound1.setLong("InhabitedTime", ichunkaccess.getInhabitedTime()); nbttagcompound1.setString("Status", ichunkaccess.getChunkStatus().d()); ChunkConverter chunkconverter = ichunkaccess.p(); -@@ -429,8 +459,8 @@ public class ChunkRegionLoader { +@@ -429,8 +452,8 @@ public class ChunkRegionLoader { NibbleArray nibblearray; // block light NibbleArray nibblearray1; // sky light if (asyncsavedata == null) { - nibblearray = lightenginethreaded.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, i)); /// Paper - diff on method change (see getAsyncSaveData) - nibblearray1 = lightenginethreaded.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, i)); // Paper - diff on method change (see getAsyncSaveData) -+ nibblearray = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? (!lightenginethreaded.hasBlockLight ? null : ichunkaccess.getBlockNibbles()[i + 1].asNibble()) : lightenginethreaded.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, i)); // Tuinity - chunk might not be loaded -+ nibblearray1 = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? (!lightenginethreaded.hasSkyLight ? null : ichunkaccess.getSkyNibbles()[i + 1].asNibble()) : lightenginethreaded.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, i)); // Tuinity - chunk might not be loaded ++ nibblearray = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? (!lightenginethreaded.hasBlockLight ? null : (ichunkaccess.getBlockNibbles()[i + 1].isAllZero() ? new NibbleArray() : ichunkaccess.getBlockNibbles()[i + 1].toVanillaNibble())) : lightenginethreaded.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, i)); // Tuinity - chunk might not be loaded ++ nibblearray1 = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? (!lightenginethreaded.hasSkyLight ? null : (ichunkaccess.getSkyNibbles()[i + 1].isAllZero() ? new NibbleArray() : ichunkaccess.getSkyNibbles()[i + 1].toVanillaNibble())) : lightenginethreaded.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, i)); // Tuinity - chunk might not be loaded } else { nibblearray = asyncsavedata.blockLight[i + 1]; // +1 to offset the -1 starting index nibblearray1 = asyncsavedata.skyLight[i + 1]; // +1 to offset the -1 starting index -@@ -444,11 +474,11 @@ public class ChunkRegionLoader { +@@ -444,12 +467,12 @@ public class ChunkRegionLoader { } if (nibblearray != null && !nibblearray.c()) { @@ -7745,24 +9071,26 @@ index 8e7da2c5f3852920ec5fbcdd2bff4d299e6aa499..64dd95292fb4d058f6200bfcadaedfbd if (nibblearray1 != null && !nibblearray1.c()) { - nbttagcompound2.setByteArray("SkyLight", nibblearray1.asBytesPoolSafe().clone()); // Paper +- } + nbttagcompound2.setByteArray("SkyLight", com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? nibblearray1.asBytes() : nibblearray1.asBytesPoolSafe().clone()); // Paper // Tuinity - data is already cloned - } ++ } else if (nibblearray1 != null && com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine) nbttagcompound2.setBoolean(UNINITIALISED_SKYLIGHT_TAG, true); // Tuinity - store uninitialised tags nbttaglist.add(nbttagcompound2); -@@ -457,7 +487,7 @@ public class ChunkRegionLoader { + } +@@ -457,7 +480,7 @@ public class ChunkRegionLoader { nbttagcompound1.set("Sections", nbttaglist); if (flag) { - nbttagcompound1.setBoolean("isLightOn", true); -+ nbttagcompound1.setBoolean("isLightOn", true); if (com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine) nbttagcompound1.setBoolean("starlight.lit", true); // Tuinity ++ nbttagcompound1.setBoolean("isLightOn", com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? false : true); if (com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine) nbttagcompound1.setInt(STARLIGHT_VERSION_TAG, STARLIGHT_LIGHT_VERSION); // Tuinity } BiomeStorage biomestorage = ichunkaccess.getBiomeIndex(); diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java -index e52df8096e399c84ff8a2637fdd65ea57d9001d0..ea943e44abb0ea9f7d471070fee14f6e5f205d53 100644 +index e52df8096e399c84ff8a2637fdd65ea57d9001d0..33b8f4e0f09fdc41c8ea48b6ed77af199136ab92 100644 --- a/src/main/java/net/minecraft/server/ChunkSection.java +++ b/src/main/java/net/minecraft/server/ChunkSection.java -@@ -11,10 +11,45 @@ public class ChunkSection { +@@ -11,7 +11,7 @@ public class ChunkSection { short nonEmptyBlockCount; // Paper - package-private short tickingBlockCount; // Paper - private -> package-private private short e; @@ -7771,62 +9099,7 @@ index e52df8096e399c84ff8a2637fdd65ea57d9001d0..ea943e44abb0ea9f7d471070fee14f6e final com.destroystokyo.paper.util.maplist.IBlockDataList tickingList = new com.destroystokyo.paper.util.maplist.IBlockDataList(); // Paper -+ // Tuinity start -+ protected int specialCollidingBlocks; -+ -+ public final boolean hasSpecialCollidingBlocks() { -+ return this.specialCollidingBlocks != 0; -+ } -+ -+ private final long[] knownBlockCollisionData = new long[16 * 16 * 16 * 2 / 64]; // blocks * bits per block / bits per long -+ public static final long KNOWN_EMPTY_BLOCK = 0b00; // known to have voxelshape of empty -+ public static final long KNOWN_FULL_BLOCK = 0b01; // known to have voxelshape of full cube -+ public static final long KNOWN_UNKNOWN_BLOCK = 0b10; // must read the actual block state for info -+ public static final long KNOWN_SPECIAL_BLOCK = 0b11; // caller must check this block for special collisions -+ -+ private final void updateKnownBlockInfo(int blockIndex, IBlockData blockData) { -+ int arrayIndex = (blockIndex >>> (6 - 1)); // blockIndex / (64/2) -+ int valueShift = (blockIndex & (Long.SIZE / 2 - 1)); -+ -+ long value = this.knownBlockCollisionData[arrayIndex]; -+ -+ value &= ~(0b11L << (valueShift << 1)); -+ value |= (blockData.getBlockCollisionBehavior() << (valueShift << 1)); -+ -+ this.knownBlockCollisionData[arrayIndex] = value; -+ } -+ -+ public final long getKnownBlockInfo(int blockIndex) { -+ int arrayIndex = (blockIndex >>> (6 - 1)); // blockIndex / (64/2) -+ int valueShift = (blockIndex & (Long.SIZE / 2 - 1)); -+ -+ long value = this.knownBlockCollisionData[arrayIndex]; -+ -+ return (value >>> (valueShift << 1)) & 0b11L; -+ } -+ // Tuinity end -+ - // Paper start - Anti-Xray - Add parameters - @Deprecated public ChunkSection(int i) { this(i, null, null, true); } // Notice for updates: Please make sure this constructor isn't used anywhere - public ChunkSection(int i, IChunkAccess chunk, World world, boolean initializeBlocks) { -@@ -62,6 +97,16 @@ public class ChunkSection { - iblockdata1 = (IBlockData) this.blockIds.b(i, j, k, iblockdata); - } - -+ // Tuinity start -+ this.updateKnownBlockInfo(i | (k << 4) | (j << 8), iblockdata); -+ if (iblockdata1.getBlockCollisionBehavior() == KNOWN_SPECIAL_BLOCK) { -+ --this.specialCollidingBlocks; -+ } -+ if (iblockdata.getBlockCollisionBehavior() == KNOWN_SPECIAL_BLOCK) { -+ ++this.specialCollidingBlocks; -+ } -+ // Tuinity end -+ - Fluid fluid = iblockdata1.getFluid(); - Fluid fluid1 = iblockdata.getFluid(); - -@@ -96,6 +141,7 @@ public class ChunkSection { +@@ -96,6 +96,7 @@ public class ChunkSection { return iblockdata1; } @@ -7834,19 +9107,6 @@ index e52df8096e399c84ff8a2637fdd65ea57d9001d0..ea943e44abb0ea9f7d471070fee14f6e public boolean c() { return this.nonEmptyBlockCount == 0; } -@@ -129,6 +175,12 @@ public class ChunkSection { - this.tickingBlockCount = 0; - this.e = 0; - this.blockIds.forEachLocation((iblockdata, location) -> { // Paper -+ // Tuinity start -+ if (iblockdata.getBlockCollisionBehavior() == KNOWN_SPECIAL_BLOCK) { -+ ++this.specialCollidingBlocks; -+ } -+ this.updateKnownBlockInfo(location, iblockdata); -+ // Tuinity end - Fluid fluid = iblockdata.getFluid(); - - if (!iblockdata.isAir()) { diff --git a/src/main/java/net/minecraft/server/ChunkStatus.java b/src/main/java/net/minecraft/server/ChunkStatus.java index f6c9bdbf52d773d7aa601125b887b347163f9328..51ea295d66312c95685b9fe4ee502a029d2fff20 100644 --- a/src/main/java/net/minecraft/server/ChunkStatus.java @@ -7944,7 +9204,7 @@ index 550232cb3819138b3bae0fa1c51429485e8bc593..229c3b0f0c650b501f31147adaa17194 throwable = throwable1; throw throwable1; diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c4d1b51c2 100644 +index d9021fde3d0908dc89384617055874ac356a8fcf..d43a843be591da1b34b37dc95d7d82549d9f56f4 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -136,7 +136,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke @@ -8098,7 +9358,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c } protected BlockPosition ap() { -@@ -815,6 +893,135 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -815,6 +893,146 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return d0; } @@ -8186,16 +9446,27 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c + try { + AxisAlignedBB collisionBox; + double stepHeight = (double)this.getStepHeight(); -+ if (stepHeight > 0.0 && (this.onGround || (moveVector.y < 0.0)) && (moveVector.x != 0.0 || moveVector.z != 0.0)) { -+ // don't bother getting the collisions if we don't need them. -+ if (moveVector.y <= 0.0) { -+ collisionBox = currBoundingBox.expand(moveVector.x, moveVector.y, moveVector.z).expandUpwards(stepHeight); ++ ++ if (moveVector.x == 0.0 && moveVector.z == 0.0 && moveVector.y != 0.0) { ++ // a lot of entities just stand still. optimise the search AABB ++ if (moveVector.y > 0.0) { ++ collisionBox = currBoundingBox.cutUpwards(moveVector.y); + } else { -+ collisionBox = currBoundingBox.expand(moveVector.x, Math.max(stepHeight, moveVector.y), moveVector.z); ++ collisionBox = currBoundingBox.cutDownwards(moveVector.y); + } + } else { -+ collisionBox = currBoundingBox.expand(moveVector.x, moveVector.y, moveVector.z); ++ if (stepHeight > 0.0 && (this.onGround || (moveVector.y < 0.0)) && (moveVector.x != 0.0 || moveVector.z != 0.0)) { ++ // don't bother getting the collisions if we don't need them. ++ if (moveVector.y <= 0.0) { ++ collisionBox = currBoundingBox.expand(moveVector.x, moveVector.y, moveVector.z).expandUpwards(stepHeight); ++ } else { ++ collisionBox = currBoundingBox.expand(moveVector.x, Math.max(stepHeight, moveVector.y), moveVector.z); ++ } ++ } else { ++ collisionBox = currBoundingBox.expand(moveVector.x, moveVector.y, moveVector.z); ++ } + } ++ + world.getCollisions(this, collisionBox, potentialCollisions, this instanceof EntityPlayer && !this.world.paperConfig.preventMovingIntoUnloadedChunks); + if (world.getWorldBorder().isCollidingWithBorderEdge(collisionBox)) { + VoxelShapes.addBoxesToIfIntersects(world.getWorldBorder().getCollisionShape(), collisionBox, potentialCollisions); @@ -8234,7 +9505,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c private Vec3D g(Vec3D vec3d) { AxisAlignedBB axisalignedbb = this.getBoundingBox(); VoxelShapeCollision voxelshapecollision = VoxelShapeCollision.a(this); -@@ -850,6 +1057,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -850,6 +1068,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return vec3d1; } @@ -8242,7 +9513,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c public static double c(Vec3D vec3d) { return vec3d.x * vec3d.x + vec3d.z * vec3d.z; } -@@ -962,18 +1170,34 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -962,18 +1181,34 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke } protected void checkBlockCollisions() { @@ -8280,7 +9551,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c try { iblockdata.a(this.world, blockposition_mutableblockposition, this); this.a(iblockdata); -@@ -987,6 +1211,11 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -987,6 +1222,11 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke } } } @@ -8292,7 +9563,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c } } -@@ -1358,6 +1587,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -1358,6 +1598,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return d3 * d3 + d4 * d4 + d5 * d5; } @@ -8300,7 +9571,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c public double h(Entity entity) { return this.e(entity.getPositionVector()); } -@@ -1944,9 +2174,9 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -1944,9 +2185,9 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke float f1 = this.size.width * 0.8F; AxisAlignedBB axisalignedbb = AxisAlignedBB.g((double) f1, 0.10000000149011612D, (double) f1).d(this.locX(), this.getHeadY(), this.locZ()); @@ -8312,7 +9583,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c } } -@@ -1954,11 +2184,13 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -1954,11 +2195,13 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return EnumInteractionResult.PASS; } @@ -8328,7 +9599,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c return false; } -@@ -2842,7 +3074,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2843,7 +3086,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke this.cp().forEach((entity) -> { worldserver.chunkCheck(entity); entity.az = true; @@ -8337,7 +9608,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c while (iterator.hasNext()) { Entity entity1 = (Entity) iterator.next(); -@@ -3300,12 +3532,16 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -3301,12 +3544,16 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return this.locBlock; } @@ -8354,7 +9625,7 @@ index e44e5652c12fbee51acedc1f911181b8443fae93..d93db1049ef9421f6b3edd0dc52a421c } public void setMot(double d0, double d1, double d2) { -@@ -3360,7 +3596,9 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -3361,7 +3608,9 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke } // Paper end if (this.loc.x != d0 || this.loc.y != d1 || this.loc.z != d2) { @@ -8396,8 +9667,21 @@ index dcc5b098bfe36ef7ee8536b3da65c4ce1748c9d8..7b32a1fb79dcae355a8d95f3a8aa4284 if (entityhuman != null) { double d0 = entityhuman.h((Entity) this); // CraftBukkit - decompile error +diff --git a/src/main/java/net/minecraft/server/EntityItem.java b/src/main/java/net/minecraft/server/EntityItem.java +index f41aaa7623c052b9f4044898d1bdee898c03057a..d99cecc4075338d7b8f154ab94d8ac04190ba371 100644 +--- a/src/main/java/net/minecraft/server/EntityItem.java ++++ b/src/main/java/net/minecraft/server/EntityItem.java +@@ -526,7 +526,7 @@ public class EntityItem extends Entity { + + // Paper start - fix MC-4 + public void setPositionRaw(double x, double y, double z) { +- if (com.destroystokyo.paper.PaperConfig.fixEntityPositionDesync) { ++ if (false && com.destroystokyo.paper.PaperConfig.fixEntityPositionDesync) { // Tuinity - revert + // encode/decode from PacketPlayOutEntity + x = MathHelper.floorLong(x * 4096.0D) * (1 / 4096.0D); + y = MathHelper.floorLong(y * 4096.0D) * (1 / 4096.0D); diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 0294ff10dc8edb24aabfbe1589048a405c356ef0..9737c8b31482de140e14e58aa9baa8618d1c3fb4 100644 +index c8cdce899b109a7f554fec7aaa8235df4224cd1d..1c396ec56f35d8764e3bf7b67a7984393eb94b3b 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -2858,7 +2858,11 @@ public abstract class EntityLiving extends Entity { @@ -8424,10 +9708,10 @@ index 0294ff10dc8edb24aabfbe1589048a405c356ef0..9737c8b31482de140e14e58aa9baa861 } diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 7240b885d96eb2df187b6229449af1a893a4524e..2c276971d47e48b39afa176994eba5747a3a3951 100644 +index 7240b885d96eb2df187b6229449af1a893a4524e..72fcd7f259630cb3c194deb685a2c4498a7c7079 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java -@@ -527,6 +527,174 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -527,6 +527,185 @@ public class EntityPlayer extends EntityHuman implements ICrafting { } } @@ -8445,7 +9729,9 @@ index 7240b885d96eb2df187b6229449af1a893a4524e..2c276971d47e48b39afa176994eba574 + int cx = centerX + dx; + int cz = centerZ + dz; + -+ if (this.getWorldServer().getChunkProvider().getChunkAtIfLoadedImmediately(cx, cz) == null) { ++ Chunk chunk = this.getWorldServer().getChunkProvider().getChunkAtIfLoadedImmediately(cx, cz); ++ ++ if (chunk == null) { + continue; + } + @@ -8455,8 +9741,8 @@ index 7240b885d96eb2df187b6229449af1a893a4524e..2c276971d47e48b39afa176994eba574 + org.bukkit.Color color; + org.bukkit.block.data.BlockData blockColor; + if (nibble == null) { -+ color = org.bukkit.Color.RED; -+ blockColor = org.bukkit.Material.RED_WOOL.createBlockData(); ++ color = org.bukkit.Color.PURPLE; ++ blockColor = org.bukkit.Material.PURPLE_WOOL.createBlockData(); + continue; + } else { + if (nibble.c()) { // is null @@ -8473,6 +9759,15 @@ index 7240b885d96eb2df187b6229449af1a893a4524e..2c276971d47e48b39afa176994eba574 + blockColor = org.bukkit.Material.ORANGE_WOOL.createBlockData(); + } + } ++ if (false) { ++ if (y < 0 || y > 15 || chunk.getSections()[y] == null || chunk.getSections()[y].isFullOfAir()) { ++ color = org.bukkit.Color.BLACK; ++ blockColor = org.bukkit.Material.BLACK_WOOL.createBlockData(); ++ } else { ++ color = org.bukkit.Color.WHITE; ++ blockColor = org.bukkit.Material.WHITE_WOOL.createBlockData(); ++ } ++ } + + org.bukkit.Particle.DustOptions dustOptions = new org.bukkit.Particle.DustOptions(color, 1.7f); + @@ -8597,7 +9892,7 @@ index 7240b885d96eb2df187b6229449af1a893a4524e..2c276971d47e48b39afa176994eba574 + + System.out.println("Block: " + this.getBukkitEntity().getLocation().getBlock().getLightFromBlocks()); + System.out.println("Sky: " + this.getBukkitEntity().getLocation().getBlock().getLightFromSky()); -+ */ // TODO remove debug ++ */ // TODO remove debug + if (this.getHealth() != this.lastHealthSent || this.lastFoodSent != this.foodData.getFoodLevel() || this.foodData.getSaturationLevel() == 0.0F != this.lastSentSaturationZero) { this.playerConnection.sendPacket(new PacketPlayOutUpdateHealth(this.getBukkitEntity().getScaledHealth(), this.foodData.getFoodLevel(), this.foodData.getSaturationLevel())); // CraftBukkit @@ -8727,10 +10022,10 @@ index c4a83448ed4513f6e4ab179d1d43e5bb0cb13641..5c3eb4fc7e5aec2ad8d0050673fc8f4d Vec3D vec3d1 = raytrace1.a(); VoxelShape voxelshape = raytrace1.a(iblockdata, this, blockposition); diff --git a/src/main/java/net/minecraft/server/IChunkAccess.java b/src/main/java/net/minecraft/server/IChunkAccess.java -index 180b6b58dc5663158db84b6f1257591439b48c31..46f9ca664782c4f68a34461dcf1cdc878b3517a8 100644 +index 180b6b58dc5663158db84b6f1257591439b48c31..2e83dd9985204b41c1e6c829d76630df4434f32e 100644 --- a/src/main/java/net/minecraft/server/IChunkAccess.java +++ b/src/main/java/net/minecraft/server/IChunkAccess.java -@@ -24,6 +24,22 @@ public interface IChunkAccess extends IBlockAccess, IStructureAccess { +@@ -24,6 +24,30 @@ public interface IChunkAccess extends IBlockAccess, IStructureAccess { } // Paper end @@ -8748,12 +10043,20 @@ index 180b6b58dc5663158db84b6f1257591439b48c31..46f9ca664782c4f68a34461dcf1cdc87 + default void setSkyNibbles(com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] nibbles) { + throw new UnsupportedOperationException(this.getClass().getName()); + } ++ // cx, cz are relative to the target chunk's map ++ public static int getEmptinessMapIndex(final int cx, final int cz) { ++ //return (cx + 1) + 3*(cz + 1); ++ return (1 + 3 * 1) + (cx) + 3*(cz); ++ } ++ public default boolean[][] getEmptinessMap() { ++ throw new UnsupportedOperationException(this.getClass().getName()); ++ } + // Tuinity end + IBlockData getType(final int x, final int y, final int z); // Paper @Nullable IBlockData setType(BlockPosition blockposition, IBlockData iblockdata, boolean flag); -@@ -122,6 +138,7 @@ public interface IChunkAccess extends IBlockAccess, IStructureAccess { +@@ -122,6 +146,7 @@ public interface IChunkAccess extends IBlockAccess, IStructureAccess { @Nullable NBTTagCompound j(BlockPosition blockposition); @@ -8761,7 +10064,7 @@ index 180b6b58dc5663158db84b6f1257591439b48c31..46f9ca664782c4f68a34461dcf1cdc87 Stream m(); TickList n(); -@@ -142,6 +159,7 @@ public interface IChunkAccess extends IBlockAccess, IStructureAccess { +@@ -142,6 +167,7 @@ public interface IChunkAccess extends IBlockAccess, IStructureAccess { return ashortlist[i]; } @@ -8932,7 +10235,7 @@ index b98e60772bad7e06845b50fdc11e98c0ea775d3d..e0bbfe1422cbad811ecb43d7436380d8 while (objectiterator.hasNext()) { entry = (Entry) objectiterator.next(); diff --git a/src/main/java/net/minecraft/server/LightEngineThreaded.java b/src/main/java/net/minecraft/server/LightEngineThreaded.java -index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f753ccf527 100644 +index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d3f02118f3f7925b64d335802f195d50e8f299e0 100644 --- a/src/main/java/net/minecraft/server/LightEngineThreaded.java +++ b/src/main/java/net/minecraft/server/LightEngineThreaded.java @@ -2,6 +2,11 @@ package net.minecraft.server; @@ -8947,17 +10250,14 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectList; import it.unimi.dsi.fastutil.objects.ObjectListIterator; -@@ -156,12 +161,244 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -156,12 +161,179 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { private volatile int f = 5; private final AtomicBoolean g = new AtomicBoolean(); + // Tuinity start - replace light engine impl -+ protected final com.tuinity.tuinity.chunk.light.ThreadedStarLightEngine theLightEngine; ++ protected final com.tuinity.tuinity.chunk.light.StarLightInterface theLightEngine; + public final boolean hasBlockLight; + public final boolean hasSkyLight; -+ -+ protected final LightEngineLayerEventListener skyReader; -+ protected final LightEngineLayerEventListener blockReader; + // Tuinity end - replace light engine impl + public LightEngineThreaded(ILightAccess ilightaccess, PlayerChunkMap playerchunkmap, boolean flag, ThreadedMailbox threadedmailbox, Mailbox> mailbox) { @@ -8968,75 +10268,13 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 + // Tuinity start - replace light engine impl + this.hasBlockLight = true; + this.hasSkyLight = flag; -+ this.theLightEngine = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? new com.tuinity.tuinity.chunk.light.ThreadedStarLightEngine(ilightaccess, flag, true) : null; -+ -+ this.blockReader = new LightEngineLayerEventListener() { -+ @Override -+ public NibbleArray a(SectionPosition sectionPosition) { -+ IChunkAccess chunk = LightEngineThreaded.this.getChunk(sectionPosition.getX(), sectionPosition.getZ()); -+ return chunk == null ? null : chunk.getBlockNibbles()[sectionPosition.getY() + 1].asNibble(); -+ } -+ -+ @Override -+ public int b(BlockPosition blockPosition) { -+ int cx = blockPosition.getX() >> 4; -+ int cy = blockPosition.getY() >> 4; -+ int cz = blockPosition.getZ() >> 4; -+ IChunkAccess chunk = LightEngineThreaded.this.getChunk(cx, cz); -+ if (chunk == null) { -+ return 0; -+ } -+ if (cy < -1 || cy > 16) { -+ return 0; -+ } -+ return chunk.getBlockNibbles()[cy + 1].getVisible(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); -+ } -+ -+ @Override -+ public void a(SectionPosition sectionPosition, boolean b) { -+ return; // don't care. -+ } -+ }; -+ if (!flag) { -+ this.skyReader = LightEngineLayerEventListener.Void.INSTANCE; -+ } else { -+ this.skyReader = new LightEngineLayerEventListener() { -+ @Override -+ public NibbleArray a(SectionPosition sectionPosition) { -+ IChunkAccess chunk = LightEngineThreaded.this.getChunk(sectionPosition.getX(), sectionPosition.getZ()); -+ return chunk == null ? null : chunk.getSkyNibbles()[sectionPosition.getY() + 1].asNibble(); -+ } -+ -+ @Override -+ public int b(BlockPosition blockPosition) { -+ int cx = blockPosition.getX() >> 4; -+ int cy = blockPosition.getY() >> 4; -+ int cz = blockPosition.getZ() >> 4; -+ IChunkAccess chunk = LightEngineThreaded.this.getChunk(cx, cz); -+ if (chunk == null) { -+ return 15; -+ } -+ if (cy < -1) { -+ cy = -1; -+ } else if (cy > 16) { -+ cy = 16; -+ } -+ com.tuinity.tuinity.chunk.light.SWMRNibbleArray nibble = chunk.getSkyNibbles()[cy + 1]; -+ return nibble.getVisible(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); -+ } -+ -+ @Override -+ public void a(SectionPosition sectionPosition, boolean b) { -+ return; // don't care. -+ } -+ }; -+ } ++ this.theLightEngine = com.tuinity.tuinity.config.TuinityConfig.useNewLightEngine ? new com.tuinity.tuinity.chunk.light.StarLightInterface(ilightaccess, flag, true) : null; + // Tuinity end - replace light engine impl + } + + // Tuinity start - replace light engine impl + protected final IChunkAccess getChunk(final int chunkX, final int chunkZ) { -+ final WorldServer world = this.theLightEngine.getWorld(); ++ final WorldServer world = (WorldServer)this.theLightEngine.getWorld(); + return world.getChunkProvider().getChunkAtImmediately(chunkX, chunkZ); + } + @@ -9173,9 +10411,9 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 + return super.a(var0); + } + if (var0 == EnumSkyBlock.BLOCK) { -+ return this.blockReader; ++ return this.theLightEngine.getBlockReader(); + } else { -+ return this.skyReader; ++ return this.theLightEngine.getSkyReader(); + } + } + @@ -9184,15 +10422,15 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 + if (this.theLightEngine == null) { + return super.b(var0, var1); + } -+ int var2 = this.skyReader.b(var0) - var1; -+ int var3 = this.blockReader.b(var0); ++ int var2 = this.theLightEngine.getSkyReader().b(var0) - var1; ++ int var3 = this.theLightEngine.getBlockReader().b(var0); + return Math.max(var3, var2); } + // Tuinity end - replace light engine impl public void close() {} -@@ -179,6 +416,15 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -179,6 +351,15 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { public void a(BlockPosition blockposition) { BlockPosition blockposition1 = blockposition.immutableCopy(); @@ -9208,7 +10446,7 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 this.a(blockposition.getX() >> 4, blockposition.getZ() >> 4, LightEngineThreaded.Update.POST_UPDATE, SystemUtils.a(() -> { super.a(blockposition1); }, () -> { -@@ -187,6 +433,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -187,6 +368,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { } protected void a(ChunkCoordIntPair chunkcoordintpair) { @@ -9220,19 +10458,22 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 this.a(chunkcoordintpair.x, chunkcoordintpair.z, () -> { return 0; }, LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { -@@ -211,6 +462,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -211,6 +397,14 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { @Override public void a(SectionPosition sectionposition, boolean flag) { -+ // Tuinity start - replace light impl ++ // Tuinity start - replace light engine impl + if (this.theLightEngine != null) { ++ this.scheduleLightWorkTask(sectionposition.getX(), sectionposition.getZ(), LightEngineThreaded.Update.POST_UPDATE, () -> { ++ this.theLightEngine.sectionChange(sectionposition, flag); ++ }); + return; + } -+ // Tuinity end - replace light impl ++ // Tuinity start - replace light engine impl this.a(sectionposition.a(), sectionposition.c(), () -> { return 0; }, LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { -@@ -222,6 +478,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -222,6 +416,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { @Override public void a(ChunkCoordIntPair chunkcoordintpair, boolean flag) { @@ -9244,7 +10485,7 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 this.a(chunkcoordintpair.x, chunkcoordintpair.z, LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { super.a(chunkcoordintpair, flag); }, () -> { -@@ -231,6 +492,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -231,6 +430,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { @Override public void a(EnumSkyBlock enumskyblock, SectionPosition sectionposition, @Nullable NibbleArray nibblearray, boolean flag) { @@ -9256,7 +10497,7 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 this.a(sectionposition.a(), sectionposition.c(), () -> { return 0; }, LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { -@@ -240,6 +506,7 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -240,6 +444,7 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { })); } @@ -9264,7 +10505,7 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 private void a(int i, int j, LightEngineThreaded.Update lightenginethreaded_update, Runnable runnable) { this.a(i, j, this.d.c(ChunkCoordIntPair.pair(i, j)), lightenginethreaded_update, runnable); } -@@ -252,6 +519,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -252,6 +457,11 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { @Override public void b(ChunkCoordIntPair chunkcoordintpair, boolean flag) { @@ -9276,21 +10517,7 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 this.a(chunkcoordintpair.x, chunkcoordintpair.z, () -> { return 0; }, LightEngineThreaded.Update.PRE_UPDATE, SystemUtils.a(() -> { -@@ -264,6 +536,13 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { - public CompletableFuture a(IChunkAccess ichunkaccess, boolean flag) { - ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); - -+ // Tuinity start - rewrite light engine -+ if (flag && this.theLightEngine != null) { -+ this.d.c(chunkcoordintpair); -+ return CompletableFuture.completedFuture(ichunkaccess); -+ } -+ // Tuinity end - rewrite light engine -+ - // Paper start - //ichunkaccess.b(false); // Don't need to disable this - long pair = chunkcoordintpair.pair(); -@@ -277,6 +556,7 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -277,6 +487,7 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { return; } // Paper end @@ -9298,12 +10525,18 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 ChunkSection[] achunksection = ichunkaccess.getSections(); for (int i = 0; i < 16; ++i) { -@@ -293,16 +573,19 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -293,16 +504,25 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { super.a(blockposition, ichunkaccess.g(blockposition)); }); } + } else { // Tuinity start - replace light engine impl -+ this.theLightEngine.lightChunk(chunkcoordintpair.x, chunkcoordintpair.z); ++ Boolean[] emptySections = com.tuinity.tuinity.chunk.light.StarLightEngine.getEmptySectionsForChunk(ichunkaccess); ++ if (flag) { ++ this.theLightEngine.loadInChunk(chunkcoordintpair.x, chunkcoordintpair.z, emptySections); ++ } else { ++ this.theLightEngine.lightChunk(chunkcoordintpair.x, chunkcoordintpair.z, emptySections); ++ } ++ + } // Tuinity end - replace light engine impl // this.d.c(chunkcoordintpair); // Paper - move into post task below @@ -9320,7 +10553,7 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 // Paper start future.complete(ichunkaccess); }); -@@ -311,7 +594,7 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -311,7 +531,7 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { } public void queueUpdate() { @@ -9329,7 +10562,7 @@ index 2f9c97dd4e1d705a87772d18c7ab4883a876af08..d4902ed0d12d9697402ca60bb8a298f7 this.b.a((() -> { // Paper - decompile error this.b(); this.g.set(false); -@@ -325,17 +608,36 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { +@@ -325,17 +545,36 @@ public class LightEngineThreaded extends LightEngine implements AutoCloseable { private final java.util.List pre = new java.util.ArrayList<>(); private final java.util.List post = new java.util.ArrayList<>(); private void b() { @@ -11437,16 +12670,17 @@ index 485b609bb5387b0f8a46c1201177cdc6d183ad91..614cfacb1e8ac5d68f0ce931933fac5d this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); // CraftBukkit - SPIGOT-5196 } diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java -index 5b0cd414ca1949ab53b289f7159f18da07d21f14..e2500821dfa7060363e851dccc12e56dfc3cb051 100644 +index 5b0cd414ca1949ab53b289f7159f18da07d21f14..42cae66f39571950929525eab592ea3feaadd587 100644 --- a/src/main/java/net/minecraft/server/ProtoChunk.java +++ b/src/main/java/net/minecraft/server/ProtoChunk.java -@@ -48,6 +48,31 @@ public class ProtoChunk implements IChunkAccess { +@@ -48,6 +48,37 @@ public class ProtoChunk implements IChunkAccess { private volatile boolean u; final World world; // Paper - Anti-Xray - Add world // Paper - private -> default + // Tuinity start - rewrite light engine -+ private volatile com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] blockNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(false); -+ private volatile com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] skyNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(true); ++ private volatile com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] blockNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(); ++ private volatile com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] skyNibbles = com.tuinity.tuinity.chunk.light.StarLightEngine.getFilledEmptyLight(); ++ private volatile boolean[][] emptinessMap = new boolean[9][]; + + @Override + public com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] getBlockNibbles() { @@ -11467,12 +12701,17 @@ index 5b0cd414ca1949ab53b289f7159f18da07d21f14..e2500821dfa7060363e851dccc12e56d + public void setSkyNibbles(com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] nibbles) { + this.skyNibbles = nibbles; + } ++ ++ @Override ++ public boolean[][] getEmptinessMap() { ++ return this.emptinessMap; ++ } + // Tuinity end - rewrite light engine + // Paper start - Anti-Xray - Add world @Deprecated public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) { this(chunkcoordintpair, chunkconverter, null); } // Notice for updates: Please make sure this constructor isn't used anywhere public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, World world) { -@@ -173,20 +198,17 @@ public class ProtoChunk implements IChunkAccess { +@@ -173,20 +204,17 @@ public class ProtoChunk implements IChunkAccess { ChunkSection chunksection = this.a(j >> 4); IBlockData iblockdata1 = chunksection.setType(i & 15, j & 15, k & 15, iblockdata); @@ -11487,17 +12726,17 @@ index 5b0cd414ca1949ab53b289f7159f18da07d21f14..e2500821dfa7060363e851dccc12e56d + HeightMap.Type[] enumset = this.getChunkStatus().heightMaps; // Tuinity - reduce iterator creation EnumSet enumset1 = null; - Iterator iterator = enumset.iterator(); -- -- HeightMap.Type heightmap_type; + // Tuinity - reduce iterator creation +- HeightMap.Type heightmap_type; +- - while (iterator.hasNext()) { - heightmap_type = (HeightMap.Type) iterator.next(); + for (HeightMap.Type heightmap_type : enumset) { // Tuinity - reduce iterator creation HeightMap heightmap = (HeightMap) this.f.get(heightmap_type); if (heightmap == null) { -@@ -202,10 +224,9 @@ public class ProtoChunk implements IChunkAccess { +@@ -202,10 +230,9 @@ public class ProtoChunk implements IChunkAccess { HeightMap.a(this, enumset1); } @@ -11512,10 +12751,10 @@ index 5b0cd414ca1949ab53b289f7159f18da07d21f14..e2500821dfa7060363e851dccc12e56d } diff --git a/src/main/java/net/minecraft/server/ProtoChunkExtension.java b/src/main/java/net/minecraft/server/ProtoChunkExtension.java -index 300cbb8b01d94e7eb0cded0c8e118103c416d4b6..60c57a2b5008b1bf4af65df09fdc0f301b8143ff 100644 +index 300cbb8b01d94e7eb0cded0c8e118103c416d4b6..5bc577683c73c50dc96d704b9c24ec821a9e7b4f 100644 --- a/src/main/java/net/minecraft/server/ProtoChunkExtension.java +++ b/src/main/java/net/minecraft/server/ProtoChunkExtension.java -@@ -8,7 +8,29 @@ import javax.annotation.Nullable; +@@ -8,7 +8,34 @@ import javax.annotation.Nullable; public class ProtoChunkExtension extends ProtoChunk { @@ -11542,6 +12781,11 @@ index 300cbb8b01d94e7eb0cded0c8e118103c416d4b6..60c57a2b5008b1bf4af65df09fdc0f30 + public void setSkyNibbles(com.tuinity.tuinity.chunk.light.SWMRNibbleArray[] nibbles) { + this.getWrappedChunk().setSkyNibbles(nibbles); + } ++ ++ @Override ++ public boolean[][] getEmptinessMap() { ++ return this.getWrappedChunk().getEmptinessMap(); ++ } + // Tuinity end - rewrite light engine public ProtoChunkExtension(Chunk chunk) { @@ -13864,7 +15108,7 @@ index f011869880fedae4b69e505491e8bdbc5f51dfba..0d10d317cd0b60fc0866ae505c7fd71f return this.j.d(); } diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d8251cc63248 100644 +index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..8d8385e3022dd84e2b92daeffa190dbda60c705b 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -55,12 +55,13 @@ import org.bukkit.event.server.MapInitializeEvent; @@ -13996,7 +15240,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 // Add env and gen to constructor, WorldData -> WorldDataServer public WorldServer(MinecraftServer minecraftserver, Executor executor, Convertable.ConversionSession convertable_conversionsession, IWorldDataServer iworlddataserver, ResourceKey resourcekey, DimensionManager dimensionmanager, WorldLoadListener worldloadlistener, ChunkGenerator chunkgenerator, boolean flag, long i, List list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { super(iworlddataserver, resourcekey, dimensionmanager, minecraftserver::getMethodProfiler, false, flag, i, gen, env, executor); // Paper pass executor -@@ -265,6 +364,303 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -265,6 +364,243 @@ public class WorldServer extends World implements GeneratorAccessSeed { this.asyncChunkTaskManager = new com.destroystokyo.paper.io.chunk.ChunkTaskManager(this); // Paper } @@ -14133,103 +15377,43 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 + continue; + } + -+ int minXIterate; -+ int maxXIterate; -+ int minZIterate; -+ int maxZIterate; -+ -+ boolean sectionHasSpecial = section.hasSpecialCollidingBlocks(); -+ if (!sectionHasSpecial && currChunkX == minChunkX) { -+ minXIterate = minX + 1; -+ } else { -+ minXIterate = minX; -+ } -+ if (!sectionHasSpecial && currChunkX == maxChunkX) { -+ maxXIterate = maxX - 1; -+ } else { -+ maxXIterate = maxX; -+ } -+ -+ if (!sectionHasSpecial && currChunkZ == minChunkZ) { -+ minZIterate = minZ + 1; -+ } else { -+ minZIterate = minZ; -+ } -+ if (!sectionHasSpecial && currChunkZ == maxChunkZ) { -+ maxZIterate = maxZ - 1; -+ } else { -+ maxZIterate = maxZ; -+ } -+ + DataPaletteBlock blocks = section.blockIds; + -+ for (int currZ = minZIterate; currZ <= maxZIterate; ++currZ) { -+ block_search_loop: -+ for (int currX = minXIterate; currX <= maxXIterate; ++currX) { ++ for (int currZ = minZ; currZ <= maxZ; ++currZ) { ++ for (int currX = minX; currX <= maxX; ++currX) { + int localBlockIndex = (currX) | (currZ << 4) | ((currY & 15) << 8); -+ long blockInfo = section.getKnownBlockInfo(localBlockIndex); -+ switch ((int)blockInfo) { -+ case (int)ChunkSection.KNOWN_EMPTY_BLOCK: { -+ continue block_search_loop; ++ int blockX = currX | chunkXGlobalPos; ++ int blockY = currY; ++ int blockZ = currZ | chunkZGlobalPos; ++ ++ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) + ++ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) + ++ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0); ++ if (edgeCount == 3) { ++ continue; ++ } ++ ++ IBlockData blockData = blocks.rawGet(localBlockIndex); ++ ++ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) { ++ mutablePos.setValues(blockX, blockY, blockZ); ++ if (collisionShape == null) { ++ collisionShape = entity == null ? VoxelShapeCollision.a() : VoxelShapeCollision.a(entity); + } -+ case (int)ChunkSection.KNOWN_FULL_BLOCK: { -+ AxisAlignedBB box = new AxisAlignedBB( -+ currX | chunkXGlobalPos, currY, currZ | chunkZGlobalPos, -+ (currX | chunkXGlobalPos) + 1, currY + 1, (currZ | chunkZGlobalPos) + 1, -+ false -+ ); -+ if (predicate != null) { -+ if (!box.voxelShapeIntersect(axisalignedbb)) { -+ continue block_search_loop; ++ VoxelShape voxelshape2 = blockData.getCollisionShape(this, mutablePos, collisionShape); ++ if (voxelshape2 != VoxelShapes.getEmptyShape()) { ++ VoxelShape voxelshape3 = voxelshape2.offset((double)blockX, (double)blockY, (double)blockZ); ++ ++ if (predicate != null && !predicate.test(blockData, mutablePos)) { ++ continue; ++ } ++ ++ if (checkOnly) { ++ if (voxelshape3.intersects(axisalignedbb)) { ++ return true; + } -+ // fall through to get the block for the predicate + } else { -+ if (box.voxelShapeIntersect(axisalignedbb)) { -+ if (checkOnly) { -+ return true; -+ } else { -+ list.add(box); -+ ret = true; -+ } -+ } -+ continue block_search_loop; -+ } -+ } -+ default: { -+ int blockX = currX | chunkXGlobalPos; -+ int blockY = currY; -+ int blockZ = currZ | chunkZGlobalPos; -+ -+ int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) + -+ ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) + -+ ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0); -+ if (edgeCount == 3 || (edgeCount != 0 && blockInfo != ChunkSection.KNOWN_SPECIAL_BLOCK)) { -+ continue block_search_loop; -+ } -+ -+ IBlockData blockData = blocks.rawGet(localBlockIndex); -+ -+ if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) { -+ mutablePos.setValues(blockX, blockY, blockZ); -+ if (collisionShape == null) { -+ collisionShape = entity == null ? VoxelShapeCollision.a() : VoxelShapeCollision.a(entity); -+ } -+ VoxelShape voxelshape2 = blockData.getCollisionShape(this, mutablePos, collisionShape); -+ if (voxelshape2 != VoxelShapes.getEmptyShape()) { -+ VoxelShape voxelshape3 = voxelshape2.offset((double)blockX, (double)blockY, (double)blockZ); -+ -+ if (predicate != null && !predicate.test(blockData, mutablePos)) { -+ continue block_search_loop; -+ } -+ -+ if (checkOnly) { -+ if (voxelshape3.intersects(axisalignedbb)) { -+ return true; -+ } -+ } else { -+ ret |= VoxelShapes.addBoxesToIfIntersects(voxelshape3, axisalignedbb, list); -+ } -+ } ++ ret |= VoxelShapes.addBoxesToIfIntersects(voxelshape3, axisalignedbb, list); + } + } + } @@ -14300,7 +15484,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 // CraftBukkit start @Override protected TileEntity getTileEntity(BlockPosition pos, boolean validate) { -@@ -318,6 +714,14 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -318,6 +654,14 @@ public class WorldServer extends World implements GeneratorAccessSeed { public void doTick(BooleanSupplier booleansupplier) { GameProfilerFiller gameprofilerfiller = this.getMethodProfiler(); @@ -14315,7 +15499,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 this.ticking = true; gameprofilerfiller.enter("world border"); -@@ -467,7 +871,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -467,7 +811,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { } timings.scheduledBlocks.stopTiming(); // Paper @@ -14324,7 +15508,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 gameprofilerfiller.exitEnter("raid"); this.timings.raids.startTiming(); // Paper - timings this.persistentRaid.a(); -@@ -476,7 +880,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -476,7 +820,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { timings.doSounds.startTiming(); // Spigot this.ak(); timings.doSounds.stopTiming(); // Spigot @@ -14333,7 +15517,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 this.ticking = false; gameprofilerfiller.exitEnter("entities"); boolean flag3 = true || !this.players.isEmpty() || !this.getForceLoadedChunks().isEmpty(); // CraftBukkit - this prevents entity cleanup, other issues on servers with no players -@@ -492,13 +896,12 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -492,13 +836,12 @@ public class WorldServer extends World implements GeneratorAccessSeed { } this.tickingEntities = true; @@ -14349,7 +15533,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 Entity entity1 = entity.getVehicle(); /* CraftBukkit start - We prevent spawning in general, so this butchering is not needed -@@ -514,6 +917,15 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -514,6 +857,15 @@ public class WorldServer extends World implements GeneratorAccessSeed { gameprofilerfiller.enter("checkDespawn"); if (!entity.dead) { entity.checkDespawn(); @@ -14365,7 +15549,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 } gameprofilerfiller.exit(); -@@ -534,14 +946,22 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -534,14 +886,22 @@ public class WorldServer extends World implements GeneratorAccessSeed { gameprofilerfiller.enter("remove"); if (entity.dead) { this.removeEntityFromChunk(entity); @@ -14389,7 +15573,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 this.tickingEntities = false; // Paper start for (java.lang.Runnable run : this.afterEntityTickingTasks) { -@@ -553,7 +973,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -553,7 +913,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { } this.afterEntityTickingTasks.clear(); // Paper end @@ -14398,7 +15582,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 Entity entity2; -@@ -563,7 +983,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -563,7 +923,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { } timings.tickEntities.stopTiming(); // Spigot @@ -14407,7 +15591,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 this.tickBlockEntities(); } -@@ -809,7 +1229,26 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -809,7 +1169,26 @@ public class WorldServer extends World implements GeneratorAccessSeed { } @@ -14434,7 +15618,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 if (!(entity instanceof EntityHuman) && !this.getChunkProvider().a(entity)) { this.chunkCheck(entity); } else { -@@ -862,6 +1301,11 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -862,6 +1241,11 @@ public class WorldServer extends World implements GeneratorAccessSeed { //} finally { timer.stopTiming(); } // Paper - timings - move up } @@ -14446,7 +15630,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 } public void a(Entity entity, Entity entity1) { -@@ -920,6 +1364,12 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -920,6 +1304,12 @@ public class WorldServer extends World implements GeneratorAccessSeed { int i = MathHelper.floor(entity.locX() / 16.0D); int j = Math.min(15, Math.max(0, MathHelper.floor(entity.locY() / 16.0D))); // Paper - stay consistent with chunk add/remove behavior int k = MathHelper.floor(entity.locZ() / 16.0D); @@ -14459,7 +15643,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 if (!entity.inChunk || entity.chunkX != i || entity.chunkY != j || entity.chunkZ != k) { // Paper start - remove entity if its in a chunk more correctly. -@@ -929,6 +1379,12 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -929,6 +1319,12 @@ public class WorldServer extends World implements GeneratorAccessSeed { } // Paper end @@ -14472,7 +15656,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 if (entity.inChunk && this.isChunkLoaded(entity.chunkX, entity.chunkZ)) { this.getChunkAt(entity.chunkX, entity.chunkZ).a(entity, entity.chunkY); } -@@ -942,6 +1398,11 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -942,6 +1338,11 @@ public class WorldServer extends World implements GeneratorAccessSeed { } else { this.getChunkAt(i, k).a(entity); } @@ -14484,7 +15668,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 } this.getMethodProfiler().exit(); -@@ -1297,7 +1758,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1297,7 +1698,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { Entity entity = (Entity) iterator.next(); if (!(entity instanceof EntityPlayer)) { @@ -14493,7 +15677,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 throw (IllegalStateException) SystemUtils.c((Throwable) (new IllegalStateException("Removing entity while ticking!"))); } -@@ -1325,6 +1786,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1325,6 +1726,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { public void unregisterEntity(Entity entity) { org.spigotmc.AsyncCatcher.catchOp("entity unregister"); // Spigot @@ -14501,7 +15685,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 // Paper start - fix entity registration issues if (entity instanceof EntityComplexPart) { // Usually this is a no-op for complex parts, and ID's should be removed, but go ahead and remove it anyways -@@ -1391,17 +1853,108 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1391,17 +1793,108 @@ public class WorldServer extends World implements GeneratorAccessSeed { this.getScoreboard().a(entity); // CraftBukkit start - SPIGOT-5278 if (entity instanceof EntityDrowned) { @@ -14613,7 +15797,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 private void registerEntity(Entity entity) { org.spigotmc.AsyncCatcher.catchOp("entity register"); // Spigot // Paper start - don't double enqueue entity registration -@@ -1412,7 +1965,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1412,7 +1905,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { return; } // Paper end @@ -14622,7 +15806,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 if (!entity.isQueuedForRegister) { // Paper this.entitiesToAdd.add(entity); entity.isQueuedForRegister = true; // Paper -@@ -1420,6 +1973,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1420,6 +1913,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { } else { entity.isQueuedForRegister = false; // Paper this.entitiesById.put(entity.getId(), entity); @@ -14630,7 +15814,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 if (entity instanceof EntityEnderDragon) { EntityComplexPart[] aentitycomplexpart = ((EntityEnderDragon) entity).eJ(); int i = aentitycomplexpart.length; -@@ -1428,6 +1982,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1428,6 +1922,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { EntityComplexPart entitycomplexpart = aentitycomplexpart[j]; this.entitiesById.put(entitycomplexpart.getId(), entitycomplexpart); @@ -14638,7 +15822,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 } } -@@ -1452,12 +2007,16 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1452,12 +1947,16 @@ public class WorldServer extends World implements GeneratorAccessSeed { // this.getChunkProvider().addEntity(entity); // Paper - moved down below valid=true // CraftBukkit start - SPIGOT-5278 if (entity instanceof EntityDrowned) { @@ -14658,7 +15842,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 } entity.valid = true; // CraftBukkit this.getChunkProvider().addEntity(entity); // Paper - from above to be below valid=true -@@ -1473,7 +2032,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1473,7 +1972,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { } public void removeEntity(Entity entity) { @@ -14667,7 +15851,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 throw (IllegalStateException) SystemUtils.c((Throwable) (new IllegalStateException("Removing entity while ticking!"))); } else { this.removeEntityFromChunk(entity); -@@ -1569,13 +2128,32 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1569,13 +2068,32 @@ public class WorldServer extends World implements GeneratorAccessSeed { @Override public void notify(BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1, int i) { @@ -14701,7 +15885,7 @@ index 5b0b6edfa790918e56399ff6c83f3feb6e5aca49..800471d450ddcb8d291dc72e93c3d825 while (iterator.hasNext()) { NavigationAbstract navigationabstract = (NavigationAbstract) iterator.next(); -@@ -1583,7 +2161,21 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1583,7 +2101,21 @@ public class WorldServer extends World implements GeneratorAccessSeed { if (!navigationabstract.i()) { navigationabstract.b(blockposition); } diff --git a/patches/server/0009-AFK-API.patch b/patches/server/0009-AFK-API.patch index ecbadabd9..74395ec25 100644 --- a/patches/server/0009-AFK-API.patch +++ b/patches/server/0009-AFK-API.patch @@ -25,10 +25,10 @@ index 3ef463e1313b9729c86437f499930ab4cd332a6d..7880c329942ff96acd1d9c713c548410 super(EntityTypes.PLAYER, world); this.bL = ItemStack.b; diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 2c276971d47e48b39afa176994eba5747a3a3951..33e3ea121c2bbcea2eb9324c9a8fc384e98a8b9b 100644 +index 72fcd7f259630cb3c194deb685a2c4498a7c7079..76d42805a4616da11c06b492590a9d52e979a4b7 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java -@@ -1893,8 +1893,54 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -1904,8 +1904,54 @@ public class EntityPlayer extends EntityHuman implements ICrafting { public void resetIdleTimer() { this.ca = SystemUtils.getMonotonicMillis(); @@ -193,10 +193,10 @@ index 37e9b8983fc310a04c98e27048b119439179cb5f..d42dbc3b44f8b54b05f356155727f5a8 if (from.getX() != Double.MAX_VALUE) { Location oldTo = to.clone(); diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 800471d450ddcb8d291dc72e93c3d8251cc63248..04578acc532849ef7c93a3515841aa05d9f7fee6 100644 +index 8d8385e3022dd84e2b92daeffa190dbda60c705b..04241410edd97bdce876a61052bd94189168d367 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java -@@ -834,7 +834,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -774,7 +774,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { // CraftBukkit end if (this.everyoneSleeping && this.players.stream().noneMatch((entityplayer) -> { @@ -205,7 +205,7 @@ index 800471d450ddcb8d291dc72e93c3d8251cc63248..04578acc532849ef7c93a3515841aa05 })) { // CraftBukkit start long l = this.worldData.getDayTime() + 24000L; -@@ -1171,7 +1171,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -1111,7 +1111,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { while (iterator.hasNext()) { EntityPlayer entityplayer = (EntityPlayer) iterator.next(); diff --git a/patches/server/0012-LivingEntity-safeFallDistance.patch b/patches/server/0012-LivingEntity-safeFallDistance.patch index 2ad5c4d70..50f35382c 100644 --- a/patches/server/0012-LivingEntity-safeFallDistance.patch +++ b/patches/server/0012-LivingEntity-safeFallDistance.patch @@ -30,7 +30,7 @@ index fcb31147622b4b81934be05ffc8de5e821ce69b7..ce26e65aaf25a41663ecd8c935967c33 protected int getChestSlots() { diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 9737c8b31482de140e14e58aa9baa8618d1c3fb4..e98e0ca17dbb683fad97be6b9f226e1ce579431f 100644 +index 1c396ec56f35d8764e3bf7b67a7984393eb94b3b..94585029142f8d8c494cf957d5363a0aec0b697d 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -132,6 +132,7 @@ public abstract class EntityLiving extends Entity { diff --git a/patches/server/0017-EntityMoveEvent.patch b/patches/server/0017-EntityMoveEvent.patch index d296fc524..faacce828 100644 --- a/patches/server/0017-EntityMoveEvent.patch +++ b/patches/server/0017-EntityMoveEvent.patch @@ -5,7 +5,7 @@ Subject: [PATCH] EntityMoveEvent diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index e98e0ca17dbb683fad97be6b9f226e1ce579431f..4bbfb21d13c7e2f3ffcaaa31bd9ceee3755aa7cc 100644 +index 94585029142f8d8c494cf957d5363a0aec0b697d..144c443f6f8b7ab2c6df2d32c27ef8bed21ce9dd 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -2813,6 +2813,20 @@ public abstract class EntityLiving extends Entity { @@ -42,7 +42,7 @@ index 5d8cf545acf231f8f9c037db26f4d29bc01efabb..fe3b76e4ed11f0183731a0dcc39a323c this.methodProfiler.a(() -> { diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 04578acc532849ef7c93a3515841aa05d9f7fee6..b8e788eb66575051073ec2230e8b71dbd59d5b71 100644 +index 04241410edd97bdce876a61052bd94189168d367..b36baf63f5981309020b3b24b907d097188b4487 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -101,6 +101,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { diff --git a/patches/server/0018-Player-invulnerabilities.patch b/patches/server/0018-Player-invulnerabilities.patch index 0518ab1d0..485e462b5 100644 --- a/patches/server/0018-Player-invulnerabilities.patch +++ b/patches/server/0018-Player-invulnerabilities.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Player invulnerabilities diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 33e3ea121c2bbcea2eb9324c9a8fc384e98a8b9b..0bcbad5612b21fab158bfe5aa124d7a2916cd7d2 100644 +index 76d42805a4616da11c06b492590a9d52e979a4b7..77603a4819c02d73d4b058a8a3d822ae6a4cb505 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -150,6 +150,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -17,7 +17,7 @@ index 33e3ea121c2bbcea2eb9324c9a8fc384e98a8b9b..0bcbad5612b21fab158bfe5aa124d7a2 } // Paper start public BlockPosition getPointInFront(double inFront) { -@@ -980,6 +982,12 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -991,6 +993,12 @@ public class EntityPlayer extends EntityHuman implements ICrafting { } @@ -30,7 +30,7 @@ index 33e3ea121c2bbcea2eb9324c9a8fc384e98a8b9b..0bcbad5612b21fab158bfe5aa124d7a2 @Override public boolean damageEntity(DamageSource damagesource, float f) { if (this.isInvulnerable(damagesource)) { -@@ -987,7 +995,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -998,7 +1006,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { } else { boolean flag = this.server.j() && this.canPvP() && "fall".equals(damagesource.translationIndex); @@ -39,7 +39,7 @@ index 33e3ea121c2bbcea2eb9324c9a8fc384e98a8b9b..0bcbad5612b21fab158bfe5aa124d7a2 return false; } else { if (damagesource instanceof EntityDamageSource) { -@@ -1158,6 +1166,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -1169,6 +1177,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { // CraftBukkit end } @@ -47,7 +47,7 @@ index 33e3ea121c2bbcea2eb9324c9a8fc384e98a8b9b..0bcbad5612b21fab158bfe5aa124d7a2 return this; } } -@@ -2311,9 +2320,17 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -2322,9 +2331,17 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @Override public boolean isFrozen() { // Paper - protected > public diff --git a/patches/server/0027-Giants-AI-settings.patch b/patches/server/0027-Giants-AI-settings.patch index 03a11c2eb..82dabb91b 100644 --- a/patches/server/0027-Giants-AI-settings.patch +++ b/patches/server/0027-Giants-AI-settings.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Giants AI settings diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index d93db1049ef9421f6b3edd0dc52a421c4d1b51c2..0502ba688d3d16f15fea1ddda1f7d0bcbe59d701 100644 +index d43a843be591da1b34b37dc95d7d82549d9f56f4..8e2bc7237463647b41e6451deb1679380a5b33f1 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -136,7 +136,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke @@ -118,7 +118,7 @@ index 7b32a1fb79dcae355a8d95f3a8aa4284ec5d10db..fba53ad6792236f0282279283570ffbd float f = difficultydamagescaler.d(); diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 4bbfb21d13c7e2f3ffcaaa31bd9ceee3755aa7cc..1fa5edd4dfbbe5cc68856a32b01a0c8ea83e3f6f 100644 +index 144c443f6f8b7ab2c6df2d32c27ef8bed21ce9dd..01c37b61a5f848588025540a20762954ea2695a5 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -165,6 +165,7 @@ public abstract class EntityLiving extends Entity { diff --git a/patches/server/0029-Zombie-horse-naturally-spawn.patch b/patches/server/0029-Zombie-horse-naturally-spawn.patch index 3a9edf28f..d2ed920df 100644 --- a/patches/server/0029-Zombie-horse-naturally-spawn.patch +++ b/patches/server/0029-Zombie-horse-naturally-spawn.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Zombie horse naturally spawn diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index b8e788eb66575051073ec2230e8b71dbd59d5b71..9469d6052ad920f9fd9848e7ee55e488372e5e62 100644 +index b36baf63f5981309020b3b24b907d097188b4487..f288bf3f6e521cfde14b08c62d591ad74d2e3153 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java -@@ -1049,12 +1049,18 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -989,12 +989,18 @@ public class WorldServer extends World implements GeneratorAccessSeed { boolean flag1 = this.getGameRules().getBoolean(GameRules.DO_MOB_SPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.b() * paperConfig.skeleHorseSpawnChance; // Paper if (flag1) { diff --git a/patches/server/0039-Cat-spawning-options.patch b/patches/server/0039-Cat-spawning-options.patch index a0cbc264f..302a23e1e 100644 --- a/patches/server/0039-Cat-spawning-options.patch +++ b/patches/server/0039-Cat-spawning-options.patch @@ -95,10 +95,10 @@ index a5718af9b614ae505067131f04ebb490617d6aa4..b6b4c8c491d692f93d2c38d602ff99b0 return this.E; } diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 9469d6052ad920f9fd9848e7ee55e488372e5e62..e26fc1a6032027d16e72931c8329196b683774ba 100644 +index f288bf3f6e521cfde14b08c62d591ad74d2e3153..93f09acd2ca26793bc0771a6cc5419bfa8536816 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java -@@ -2559,6 +2559,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -2499,6 +2499,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { } } diff --git a/patches/server/0041-Cows-eat-mushrooms.patch b/patches/server/0041-Cows-eat-mushrooms.patch index c8704e773..f194c7cec 100644 --- a/patches/server/0041-Cows-eat-mushrooms.patch +++ b/patches/server/0041-Cows-eat-mushrooms.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Cows eat mushrooms diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 0502ba688d3d16f15fea1ddda1f7d0bcbe59d701..bcd70f3d871ef8ef957fde3bb86c5ae29dce3d2f 100644 +index 8e2bc7237463647b41e6451deb1679380a5b33f1..3d1c3232632e49ae2d7a22c9a48545540200efa0 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java -@@ -2775,6 +2775,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2786,6 +2786,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke this.invulnerable = flag; } @@ -110,7 +110,7 @@ index 42e6761c8b18b79ffd3f4d5e853ea87a2c153c23..cfb009c811bd2908d38da1b0007cb7aa public EntityCow createChild(WorldServer worldserver, EntityAgeable entityageable) { return (EntityCow) EntityTypes.COW.a((World) worldserver); diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 1fa5edd4dfbbe5cc68856a32b01a0c8ea83e3f6f..f7bdd5f2a35c0abc018732d7fd920b162cb0a2d1 100644 +index 01c37b61a5f848588025540a20762954ea2695a5..5ab1121c99c67662ce659428be46f2189f7e2d04 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -80,7 +80,7 @@ public abstract class EntityLiving extends Entity { diff --git a/patches/server/0048-Signs-allow-color-codes.patch b/patches/server/0048-Signs-allow-color-codes.patch index 05c745b57..d446a964f 100644 --- a/patches/server/0048-Signs-allow-color-codes.patch +++ b/patches/server/0048-Signs-allow-color-codes.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Signs allow color codes diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 0bcbad5612b21fab158bfe5aa124d7a2916cd7d2..6323b3661a04de3206d0ac762599a3f5709a00ff 100644 +index 77603a4819c02d73d4b058a8a3d822ae6a4cb505..da693a31f26973467b4494e79996c3f0d8a3a758 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java -@@ -1418,6 +1418,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -1429,6 +1429,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @Override public void openSign(TileEntitySign tileentitysign) { diff --git a/patches/server/0050-Controllable-Minecarts.patch b/patches/server/0050-Controllable-Minecarts.patch index 54ed011a2..9ddbf951c 100644 --- a/patches/server/0050-Controllable-Minecarts.patch +++ b/patches/server/0050-Controllable-Minecarts.patch @@ -22,7 +22,7 @@ index 2291135eaef64c403183724cb6e413cd7e472672..bc61aaff65a7dc1e7534452b285953b8 super(i, j, k); } diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index f7bdd5f2a35c0abc018732d7fd920b162cb0a2d1..15518abcc1ec5f9a56d1992c852d180c9937fc69 100644 +index 5ab1121c99c67662ce659428be46f2189f7e2d04..802d5488876a4bebad18fbb6b2256c0259ba50c2 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -98,9 +98,9 @@ public abstract class EntityLiving extends Entity { @@ -106,10 +106,10 @@ index efe5c0cecaf12ef921f6d32ff6670eff051bf323..a814cf280d1ce0d446d24da8e596f13e this.move(EnumMoveType.SELF, this.getMot()); if (!this.onGround) { diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 6323b3661a04de3206d0ac762599a3f5709a00ff..0ad636b88dfcd955621c1db56f73333300afcb88 100644 +index da693a31f26973467b4494e79996c3f0d8a3a758..5bee05e6e8b52d1edea81a0d76ec9cfdb5acf704 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java -@@ -993,6 +993,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -1004,6 +1004,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { if (this.isInvulnerable(damagesource)) { return false; } else { diff --git a/patches/server/0051-Disable-loot-drops-on-death-by-cramming.patch b/patches/server/0051-Disable-loot-drops-on-death-by-cramming.patch index 73ead41fd..56efc5408 100644 --- a/patches/server/0051-Disable-loot-drops-on-death-by-cramming.patch +++ b/patches/server/0051-Disable-loot-drops-on-death-by-cramming.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Disable loot drops on death by cramming diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 15518abcc1ec5f9a56d1992c852d180c9937fc69..39342dd0bb7d79167310d61b629718b150595614 100644 +index 802d5488876a4bebad18fbb6b2256c0259ba50c2..737b812d3932de29233b26fe7a978b98b2c3cc8f 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -1493,8 +1493,10 @@ public abstract class EntityLiving extends Entity { diff --git a/patches/server/0052-Players-should-not-cram-to-death.patch b/patches/server/0052-Players-should-not-cram-to-death.patch index c1571120b..bbd5e7fe6 100644 --- a/patches/server/0052-Players-should-not-cram-to-death.patch +++ b/patches/server/0052-Players-should-not-cram-to-death.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Players should not cram to death diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 0ad636b88dfcd955621c1db56f73333300afcb88..65b95816f885129b83675161478ab19b4a7de8bc 100644 +index 5bee05e6e8b52d1edea81a0d76ec9cfdb5acf704..36be65f9a36ea1b35ffa882eec295e1f46021f0c 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java -@@ -1395,7 +1395,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -1406,7 +1406,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @Override public boolean isInvulnerable(DamageSource damagesource) { diff --git a/patches/server/0055-Fix-the-dead-lagging-the-server.patch b/patches/server/0055-Fix-the-dead-lagging-the-server.patch index a394260f1..6ba6547f0 100644 --- a/patches/server/0055-Fix-the-dead-lagging-the-server.patch +++ b/patches/server/0055-Fix-the-dead-lagging-the-server.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Fix the dead lagging the server diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index bcd70f3d871ef8ef957fde3bb86c5ae29dce3d2f..01cb91589912e1d3e4b1edf9cdc8fabfc767d0dc 100644 +index 3d1c3232632e49ae2d7a22c9a48545540200efa0..cbdfc8d929f9419d49b0642e8cfc7219741e0e38 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java -@@ -1521,6 +1521,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -1532,6 +1532,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke this.pitch = MathHelper.a(f1, -90.0F, 90.0F) % 360.0F; this.lastYaw = this.yaw; this.lastPitch = this.pitch; @@ -17,7 +17,7 @@ index bcd70f3d871ef8ef957fde3bb86c5ae29dce3d2f..01cb91589912e1d3e4b1edf9cdc8fabf public void f(double d0, double d1, double d2) { diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 39342dd0bb7d79167310d61b629718b150595614..7bffafb7b2b6124b8ca1400e99fa5973e96793f2 100644 +index 737b812d3932de29233b26fe7a978b98b2c3cc8f..435726651ada6ccb4bbd31fe21a439d29f131098 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -2492,7 +2492,7 @@ public abstract class EntityLiving extends Entity { diff --git a/patches/server/0064-Villagers-follow-emerald-blocks.patch b/patches/server/0064-Villagers-follow-emerald-blocks.patch index e99093065..d5ce63a53 100644 --- a/patches/server/0064-Villagers-follow-emerald-blocks.patch +++ b/patches/server/0064-Villagers-follow-emerald-blocks.patch @@ -23,7 +23,7 @@ index 171e8553a339eb3c995369f274de86b824183ca6..0ab7d3441bcafae9874134c7832a4b7c public BehaviorController getBehaviorController() { return (BehaviorController) super.getBehaviorController(); // CraftBukkit - decompile error diff --git a/src/main/java/net/minecraft/server/EntityVillagerAbstract.java b/src/main/java/net/minecraft/server/EntityVillagerAbstract.java -index 1fddf7c77488a5e53fc48d0db0a7b8acc71e2f42..488a6891d83b9673b5f9590e14283c44c0015316 100644 +index 8971dd013498e70c4e7a6ac6a218f62e599c86b9..3e912ef9cce8ded55564dbfe61329192922b2f13 100644 --- a/src/main/java/net/minecraft/server/EntityVillagerAbstract.java +++ b/src/main/java/net/minecraft/server/EntityVillagerAbstract.java @@ -14,6 +14,8 @@ import org.bukkit.event.entity.VillagerAcquireTradeEvent; diff --git a/patches/server/0070-PaperPR-Projectile-load-save-limit-per-chunk.patch b/patches/server/0070-PaperPR-Projectile-load-save-limit-per-chunk.patch index 33cb5b0e8..3668105d1 100644 --- a/patches/server/0070-PaperPR-Projectile-load-save-limit-per-chunk.patch +++ b/patches/server/0070-PaperPR-Projectile-load-save-limit-per-chunk.patch @@ -23,10 +23,10 @@ index ae5ed3bd0b663092a4658b24cbd69d37b4e141cb..2452f54d96cab2d93140c64e25d9b799 + } } diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 64dd95292fb4d058f6200bfcadaedfbd62b2461d..3628b04fc4f4f4663b3780eadac6ff9e430e4900 100644 +index 1f1f1a0cb84a07087025fd831441d0571d6de7c6..423da11d4910bbe12625ec5a16edfd6e80cd3dea 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -@@ -525,11 +525,21 @@ public class ChunkRegionLoader { +@@ -518,11 +518,21 @@ public class ChunkRegionLoader { chunk.d(false); @@ -48,7 +48,7 @@ index 64dd95292fb4d058f6200bfcadaedfbd62b2461d..3628b04fc4f4f4663b3780eadac6ff9e NBTTagCompound nbttagcompound4 = new NBTTagCompound(); // Paper start if (asyncsavedata == null && !entity.dead && (int) Math.floor(entity.locX()) >> 4 != chunk.getPos().x || (int) Math.floor(entity.locZ()) >> 4 != chunk.getPos().z) { -@@ -660,10 +670,20 @@ public class ChunkRegionLoader { +@@ -653,10 +663,20 @@ public class ChunkRegionLoader { NBTTagList nbttaglist = nbttagcompound.getList("Entities", 10); World world = chunk.getWorld(); diff --git a/patches/server/0071-Add-canSaveToDisk-to-Entity.patch b/patches/server/0071-Add-canSaveToDisk-to-Entity.patch index 2188670f1..8ddff2126 100644 --- a/patches/server/0071-Add-canSaveToDisk-to-Entity.patch +++ b/patches/server/0071-Add-canSaveToDisk-to-Entity.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add canSaveToDisk to Entity diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 3628b04fc4f4f4663b3780eadac6ff9e430e4900..b40cb0c7b25a9ef25dddcf01ab7b5e97fe68655d 100644 +index 423da11d4910bbe12625ec5a16edfd6e80cd3dea..77f31dc1e45b438ece691617deb17d7a5fbbc94b 100644 --- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java +++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -@@ -532,6 +532,7 @@ public class ChunkRegionLoader { +@@ -525,6 +525,7 @@ public class ChunkRegionLoader { while (iterator1.hasNext()) { Entity entity = (Entity) iterator1.next(); @@ -17,7 +17,7 @@ index 3628b04fc4f4f4663b3780eadac6ff9e430e4900..b40cb0c7b25a9ef25dddcf01ab7b5e97 final EntityTypes projectileType = entity.getEntityType(); if (savedProjectileCounts.getOrDefault(projectileType, 0) >= worldserver.paperConfig.projectileSaveLimit) { diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 01cb91589912e1d3e4b1edf9cdc8fabfc767d0dc..ca7189dbf4f8e71163de55ee89cad15390744f96 100644 +index cbdfc8d929f9419d49b0642e8cfc7219741e0e38..85f7c97136195bd7c77685244e89ceedec72dee5 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -308,6 +308,12 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke diff --git a/patches/server/0072-Configurable-void-damage-height.patch b/patches/server/0072-Configurable-void-damage-height.patch index e9b729fed..a51708221 100644 --- a/patches/server/0072-Configurable-void-damage-height.patch +++ b/patches/server/0072-Configurable-void-damage-height.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Configurable void damage height diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index ca7189dbf4f8e71163de55ee89cad15390744f96..ea1bccdc776c6eddbad1407cdc31a889875c8909 100644 +index 85f7c97136195bd7c77685244e89ceedec72dee5..2e6b621694c4bbaeb2d7e3e2773bacda8374aa0d 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -611,7 +611,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke diff --git a/patches/server/0079-Implement-elytra-settings.patch b/patches/server/0079-Implement-elytra-settings.patch index b54f5aff1..13c86e2f5 100644 --- a/patches/server/0079-Implement-elytra-settings.patch +++ b/patches/server/0079-Implement-elytra-settings.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Implement elytra settings diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 7bffafb7b2b6124b8ca1400e99fa5973e96793f2..93512ebabf8ab4626dea50d7a3e80fa3333a1e91 100644 +index 435726651ada6ccb4bbd31fe21a439d29f131098..be5f2147e9cded85620a57a90c7d800262fc3d6e 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -2851,7 +2851,16 @@ public abstract class EntityLiving extends Entity { diff --git a/patches/server/0080-Item-entity-immunities.patch b/patches/server/0080-Item-entity-immunities.patch index 6df6dca08..ae1ad753c 100644 --- a/patches/server/0080-Item-entity-immunities.patch +++ b/patches/server/0080-Item-entity-immunities.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Item entity immunities diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index ea1bccdc776c6eddbad1407cdc31a889875c8909..c2692d6d667d6404a044f1ad1c24e612b6efcd30 100644 +index 2e6b621694c4bbaeb2d7e3e2773bacda8374aa0d..308cb9cfb418e63f17f8f39c3db94f93d874763f 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java -@@ -1470,6 +1470,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -1481,6 +1481,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke } @@ -17,7 +17,7 @@ index ea1bccdc776c6eddbad1407cdc31a889875c8909..c2692d6d667d6404a044f1ad1c24e612 return this.O == tag; } diff --git a/src/main/java/net/minecraft/server/EntityItem.java b/src/main/java/net/minecraft/server/EntityItem.java -index f41aaa7623c052b9f4044898d1bdee898c03057a..1ab024c19761d97d0ac81edcc3fb1d9be8c40b5f 100644 +index d99cecc4075338d7b8f154ab94d8ac04190ba371..cbe03923d468bf75adc10245ebd9f63ff10987f9 100644 --- a/src/main/java/net/minecraft/server/EntityItem.java +++ b/src/main/java/net/minecraft/server/EntityItem.java @@ -23,6 +23,8 @@ public class EntityItem extends Entity { diff --git a/patches/server/0083-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch b/patches/server/0083-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch index 61aa94483..d1f873443 100644 --- a/patches/server/0083-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch +++ b/patches/server/0083-Phantoms-attracted-to-crystals-and-crystals-shoot-ph.patch @@ -17,10 +17,10 @@ index 6fe5678cffc2487fe00c953d772f764bb37a4b11..bd0267ee4b3782f6d1ec39cba7966ba4 return (new EntityDamageSourceIndirect("indirectMagic", entity, entity1)).setIgnoreArmor().setMagic(); } diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index c2692d6d667d6404a044f1ad1c24e612b6efcd30..477e95ebecea183a2ad3d1daac3b4010b7263c99 100644 +index 308cb9cfb418e63f17f8f39c3db94f93d874763f..d542cd8014a929b421e631075f1ee14423e9b66a 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java -@@ -2135,8 +2135,8 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2146,8 +2146,8 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return this.a(new ItemStack(imaterial), (float) i); } diff --git a/patches/server/0090-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch b/patches/server/0090-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch index 3a513798e..28ae8cb8f 100644 --- a/patches/server/0090-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch +++ b/patches/server/0090-Add-option-to-teleport-to-spawn-if-outside-world-bor.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add option to teleport to spawn if outside world border diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 93512ebabf8ab4626dea50d7a3e80fa3333a1e91..ef1c5fc24c2ffd2939e9fc10eb15ef96d5f0a3ca 100644 +index be5f2147e9cded85620a57a90c7d800262fc3d6e..699afb9759083b42fd0608a3417fd3d17ce8f560 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -280,6 +280,7 @@ public abstract class EntityLiving extends Entity { @@ -17,10 +17,10 @@ index 93512ebabf8ab4626dea50d7a3e80fa3333a1e91..ef1c5fc24c2ffd2939e9fc10eb15ef96 } } diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 65b95816f885129b83675161478ab19b4a7de8bc..1a8f894fc077d77911c8e70675b647b7c28e20b7 100644 +index 36be65f9a36ea1b35ffa882eec295e1f46021f0c..40d2494114348ea256883cb4e8ebd3e02b040b14 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java -@@ -2378,4 +2378,26 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -2389,4 +2389,26 @@ public class EntityPlayer extends EntityHuman implements ICrafting { return (CraftPlayer) super.getBukkitEntity(); } // CraftBukkit end diff --git a/patches/server/0097-Totems-work-in-inventory.patch b/patches/server/0097-Totems-work-in-inventory.patch index 6d37c13dd..85f51f1f3 100644 --- a/patches/server/0097-Totems-work-in-inventory.patch +++ b/patches/server/0097-Totems-work-in-inventory.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Totems work in inventory diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index ef1c5fc24c2ffd2939e9fc10eb15ef96d5f0a3ca..64580ce8dc1d0be61615a47c2fb3a6b0fecc93e6 100644 +index 699afb9759083b42fd0608a3417fd3d17ce8f560..f1e1db97d611192ff2d4b635ef81cf592adde257 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -1327,6 +1327,19 @@ public abstract class EntityLiving extends Entity { diff --git a/patches/server/0098-Fix-death-message-colors.patch b/patches/server/0098-Fix-death-message-colors.patch index decf723ec..8259c8129 100644 --- a/patches/server/0098-Fix-death-message-colors.patch +++ b/patches/server/0098-Fix-death-message-colors.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Fix death message colors diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 1a8f894fc077d77911c8e70675b647b7c28e20b7..7a0d93538fbfcc559acc0ac5897aa05b4d39839f 100644 +index 40d2494114348ea256883cb4e8ebd3e02b040b14..d8cb09e809e382c48071d8abc3d952ca4ef64c9b 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java -@@ -841,7 +841,24 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -852,7 +852,24 @@ public class EntityPlayer extends EntityHuman implements ICrafting { IChatBaseComponent defaultMessage = this.getCombatTracker().getDeathMessage(); diff --git a/patches/server/0106-Add-no-tick-block-list.patch b/patches/server/0106-Add-no-tick-block-list.patch index d19342cdc..517d730e5 100644 --- a/patches/server/0106-Add-no-tick-block-list.patch +++ b/patches/server/0106-Add-no-tick-block-list.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add no-tick block list diff --git a/src/main/java/net/minecraft/server/BlockBase.java b/src/main/java/net/minecraft/server/BlockBase.java -index 4d1ac4e6b61897fc03b091475ef7be3ed0b228a9..483756316a51780da2122b68e73ffc5fc9d87df3 100644 +index 829d4a7508e1656dbdc912096b7eafcf30cbb5b2..6aea156d7c7a9ca8a357aad6a6781d7209c9b8ae 100644 --- a/src/main/java/net/minecraft/server/BlockBase.java +++ b/src/main/java/net/minecraft/server/BlockBase.java -@@ -655,10 +655,12 @@ public abstract class BlockBase { +@@ -617,10 +617,12 @@ public abstract class BlockBase { } public void a(WorldServer worldserver, BlockPosition blockposition, Random random) { @@ -22,7 +22,7 @@ index 4d1ac4e6b61897fc03b091475ef7be3ed0b228a9..483756316a51780da2122b68e73ffc5f } diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index e26fc1a6032027d16e72931c8329196b683774ba..9d93a8c86b675cf55210724a0b695f960b009d69 100644 +index 93f09acd2ca26793bc0771a6cc5419bfa8536816..d86fa84b48d2c2b3bf4619788bdc64d22212661d 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -314,14 +314,14 @@ public class WorldServer extends World implements GeneratorAccessSeed { diff --git a/patches/server/0109-Stop-squids-floating-on-top-of-water.patch b/patches/server/0109-Stop-squids-floating-on-top-of-water.patch index df89e1855..4bc0373c7 100644 --- a/patches/server/0109-Stop-squids-floating-on-top-of-water.patch +++ b/patches/server/0109-Stop-squids-floating-on-top-of-water.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Stop squids floating on top of water diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 477e95ebecea183a2ad3d1daac3b4010b7263c99..a12bcda00cb615b0832157f633c4a987e78c8404 100644 +index d542cd8014a929b421e631075f1ee14423e9b66a..bbe567dad1212ad3a49503ca9b33c3f28c58a16d 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java -@@ -3438,8 +3438,13 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -3450,8 +3450,13 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke this.lastYaw = this.yaw; } diff --git a/patches/server/0110-Ridables.patch b/patches/server/0110-Ridables.patch index 772624ac7..34274bbb2 100644 --- a/patches/server/0110-Ridables.patch +++ b/patches/server/0110-Ridables.patch @@ -161,7 +161,7 @@ index bd0267ee4b3782f6d1ec39cba7966ba4f62f1adf..8b36ac2b0950a827763aa2357700f37e this.B = true; return this; diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index a12bcda00cb615b0832157f633c4a987e78c8404..3eef1534c0dc3c6089f0d619d42784775fb1626e 100644 +index bbe567dad1212ad3a49503ca9b33c3f28c58a16d..0be1f47b13f617a7aaad4d6334929d9e5f943d3e 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -80,7 +80,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke @@ -200,7 +200,7 @@ index a12bcda00cb615b0832157f633c4a987e78c8404..3eef1534c0dc3c6089f0d619d4278477 private float headHeight; // CraftBukkit start public boolean persist = true; -@@ -1480,6 +1480,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -1491,6 +1491,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return !this.justCreated && this.M.getDouble(TagsFluid.LAVA) > 0.0D; } @@ -208,7 +208,7 @@ index a12bcda00cb615b0832157f633c4a987e78c8404..3eef1534c0dc3c6089f0d619d4278477 public void a(float f, Vec3D vec3d) { Vec3D vec3d1 = a(vec3d, f, this.yaw); -@@ -2235,6 +2236,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2246,6 +2247,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return this.a(entity, false); } @@ -216,7 +216,7 @@ index a12bcda00cb615b0832157f633c4a987e78c8404..3eef1534c0dc3c6089f0d619d4278477 public boolean a(Entity entity, boolean flag) { for (Entity entity1 = entity; entity1.vehicle != null; entity1 = entity1.vehicle) { if (entity1.vehicle == this) { -@@ -2330,6 +2332,13 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2341,6 +2343,13 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke this.passengers.add(entity); } @@ -230,7 +230,7 @@ index a12bcda00cb615b0832157f633c4a987e78c8404..3eef1534c0dc3c6089f0d619d4278477 } return true; // CraftBukkit } -@@ -2370,6 +2379,12 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2381,6 +2390,12 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return false; } // Spigot end @@ -243,7 +243,7 @@ index a12bcda00cb615b0832157f633c4a987e78c8404..3eef1534c0dc3c6089f0d619d4278477 this.passengers.remove(entity); entity.j = 60; } -@@ -2535,6 +2550,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2546,6 +2561,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke this.setFlag(4, flag); } @@ -251,7 +251,7 @@ index a12bcda00cb615b0832157f633c4a987e78c8404..3eef1534c0dc3c6089f0d619d4278477 public boolean bE() { return this.glowing || this.world.isClientSide && this.getFlag(6); } -@@ -2757,6 +2773,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2768,6 +2784,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke public void setHeadRotation(float f) {} @@ -259,7 +259,7 @@ index a12bcda00cb615b0832157f633c4a987e78c8404..3eef1534c0dc3c6089f0d619d4278477 public void n(float f) {} public boolean bL() { -@@ -3191,6 +3208,18 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -3203,6 +3220,18 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return false; } @@ -278,7 +278,7 @@ index a12bcda00cb615b0832157f633c4a987e78c8404..3eef1534c0dc3c6089f0d619d4278477 @Override public void sendMessage(IChatBaseComponent ichatbasecomponent, UUID uuid) {} -@@ -3643,4 +3672,47 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -3655,4 +3684,47 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return ((ChunkProviderServer) world.getChunkProvider()).isInEntityTickingChunk(this); } // Paper end @@ -2372,7 +2372,7 @@ index bdff2368836dca230a6622a205d5772834afc6ee..9ee03b233b71d1b4b85a9a5e1f0ea9fe float f1 = 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F; diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 64580ce8dc1d0be61615a47c2fb3a6b0fecc93e6..418744b166571973a02dd6a31f0cc08376980076 100644 +index f1e1db97d611192ff2d4b635ef81cf592adde257..81e362c6e0b9e6dde5df3f9009ecc8fdc1943789 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -462,7 +462,7 @@ public abstract class EntityLiving extends Entity { @@ -3211,7 +3211,7 @@ index a3a428da99574c485fcf2b8c7944e0d8354146ee..cf7de0127166f6175a6246062c8664e6 this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, true)); this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityVillagerAbstract.class, false)); diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 7a0d93538fbfcc559acc0ac5897aa05b4d39839f..013f281a686adef9a678ace4051c5e51ab677378 100644 +index d8cb09e809e382c48071d8abc3d952ca4ef64c9b..beb0a4b91a8abd0374ae40771009832ddf8978f7 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -508,6 +508,15 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -5041,7 +5041,7 @@ index 7a82a894bb3f737cc80f0b4b8d7a1b25ce1afc6c..bdfdb73bc1305d3fbda2e0ebe8163bf4 // Purpur end } diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 9d93a8c86b675cf55210724a0b695f960b009d69..bed00077205d3c2d7b2eb67db5af86ddc5346cc2 100644 +index d86fa84b48d2c2b3bf4619788bdc64d22212661d..c402803a364908b854efdb5777f355ac5fee51a4 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -102,6 +102,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { diff --git a/patches/server/0112-Crying-obsidian-valid-for-portal-frames.patch b/patches/server/0112-Crying-obsidian-valid-for-portal-frames.patch index 060522273..06c73d115 100644 --- a/patches/server/0112-Crying-obsidian-valid-for-portal-frames.patch +++ b/patches/server/0112-Crying-obsidian-valid-for-portal-frames.patch @@ -17,10 +17,10 @@ index 1fc98698b81c079ebe4a524200232db1fe143bdf..d621b11ba9029a732a819e0558d6f8a4 return this == block; } diff --git a/src/main/java/net/minecraft/server/BlockBase.java b/src/main/java/net/minecraft/server/BlockBase.java -index 483756316a51780da2122b68e73ffc5fc9d87df3..5550693a4ea0c1d99abb35b826d09963bc6c45fd 100644 +index 6aea156d7c7a9ca8a357aad6a6781d7209c9b8ae..b40f5167d2a9772658c115091f13706fbb4959b7 100644 --- a/src/main/java/net/minecraft/server/BlockBase.java +++ b/src/main/java/net/minecraft/server/BlockBase.java -@@ -725,6 +725,7 @@ public abstract class BlockBase { +@@ -687,6 +687,7 @@ public abstract class BlockBase { return this.getBlock().a(tag) && predicate.test(this); } diff --git a/patches/server/0113-Entities-can-use-portals-configuration.patch b/patches/server/0113-Entities-can-use-portals-configuration.patch index dfae7dddd..6040d1156 100644 --- a/patches/server/0113-Entities-can-use-portals-configuration.patch +++ b/patches/server/0113-Entities-can-use-portals-configuration.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Entities can use portals configuration diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 3eef1534c0dc3c6089f0d619d42784775fb1626e..28c502b5721bd150d9b9d2d84cb07c55af8373f1 100644 +index 0be1f47b13f617a7aaad4d6334929d9e5f943d3e..b882c049870e461472fe3252922f206c6045dbff 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java -@@ -2411,7 +2411,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2422,7 +2422,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke public void d(BlockPosition blockposition) { if (this.ai()) { this.resetPortalCooldown(); @@ -17,7 +17,7 @@ index 3eef1534c0dc3c6089f0d619d42784775fb1626e..28c502b5721bd150d9b9d2d84cb07c55 if (!this.world.isClientSide && !blockposition.equals(this.ac)) { this.ac = blockposition.immutableCopy(); } -@@ -2984,7 +2984,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke +@@ -2996,7 +2996,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke } public boolean canPortal() { diff --git a/patches/server/0116-Allow-toggling-special-MobSpawners-per-world.patch b/patches/server/0116-Allow-toggling-special-MobSpawners-per-world.patch index b099c633c..ae28cbf48 100644 --- a/patches/server/0116-Allow-toggling-special-MobSpawners-per-world.patch +++ b/patches/server/0116-Allow-toggling-special-MobSpawners-per-world.patch @@ -42,7 +42,7 @@ index bdfdb73bc1305d3fbda2e0ebe8163bf43b8aede5..ab97d076c921e1fd3ba69ed4b93d43e9 this.generator = gen; this.world = new CraftWorld((WorldServer) this, gen, env); diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index bed00077205d3c2d7b2eb67db5af86ddc5346cc2..a1069b64b3af02cbd5d2808275c09d7680c7c1c6 100644 +index c402803a364908b854efdb5777f355ac5fee51a4..9eceeb44b19533d80f591850ab496b24b3605a4b 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -332,7 +332,24 @@ public class WorldServer extends World implements GeneratorAccessSeed { diff --git a/patches/server/0126-Configurable-daylight-cycle.patch b/patches/server/0126-Configurable-daylight-cycle.patch index b9d9ca097..4fee9827b 100644 --- a/patches/server/0126-Configurable-daylight-cycle.patch +++ b/patches/server/0126-Configurable-daylight-cycle.patch @@ -18,7 +18,7 @@ index 88c3d7efaf467c1c1487f589c2cdbfb6aba734ec..fed1ce95038ead72a663f5a562dd4628 public PacketPlayOutUpdateTime() {} diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index a1069b64b3af02cbd5d2808275c09d7680c7c1c6..790473d51ee49340f3f2b17a195e63902a2bf779 100644 +index 9eceeb44b19533d80f591850ab496b24b3605a4b..0789281a18fd9dc8aab2def638f45510dc29dabc 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -94,6 +94,7 @@ public class WorldServer extends World implements GeneratorAccessSeed { @@ -37,7 +37,7 @@ index a1069b64b3af02cbd5d2808275c09d7680c7c1c6..790473d51ee49340f3f2b17a195e6390 } // Tuinity start - optimise collision -@@ -1017,7 +1019,21 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -957,7 +959,21 @@ public class WorldServer extends World implements GeneratorAccessSeed { this.nextTickListBlock.nextTick(); // Paper this.nextTickListFluid.nextTick(); // Paper this.worldDataServer.u().a(this.server, i); @@ -60,7 +60,7 @@ index a1069b64b3af02cbd5d2808275c09d7680c7c1c6..790473d51ee49340f3f2b17a195e6390 this.setDayTime(this.worldData.getDayTime() + 1L); } -@@ -1026,6 +1042,12 @@ public class WorldServer extends World implements GeneratorAccessSeed { +@@ -966,6 +982,12 @@ public class WorldServer extends World implements GeneratorAccessSeed { public void setDayTime(long i) { this.worldDataServer.setDayTime(i); diff --git a/patches/server/0130-Add-tablist-suffix-option-for-afk.patch b/patches/server/0130-Add-tablist-suffix-option-for-afk.patch index 1d37672a0..1058bf9da 100644 --- a/patches/server/0130-Add-tablist-suffix-option-for-afk.patch +++ b/patches/server/0130-Add-tablist-suffix-option-for-afk.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add tablist suffix option for afk diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 013f281a686adef9a678ace4051c5e51ab677378..988e8545d4f9bf0fb0b8b8b482a29775b8a5c9f1 100644 +index beb0a4b91a8abd0374ae40771009832ddf8978f7..3906805c5d6b9203e1ad160207154493660c8188 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java -@@ -1961,7 +1961,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -1972,7 +1972,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting { } if (world.purpurConfig.idleTimeoutUpdateTabList) { diff --git a/patches/server/0134-Apply-display-names-from-item-forms-of-entities-to-e.patch b/patches/server/0134-Apply-display-names-from-item-forms-of-entities-to-e.patch index b180ffc97..9ddf221ea 100644 --- a/patches/server/0134-Apply-display-names-from-item-forms-of-entities-to-e.patch +++ b/patches/server/0134-Apply-display-names-from-item-forms-of-entities-to-e.patch @@ -86,7 +86,7 @@ index ace2bb69fcde9af2a1691a25ae945bfe44ce117b..2169de3c4118fca7048f9939a9570939 } } diff --git a/src/main/java/net/minecraft/server/EntityItemFrame.java b/src/main/java/net/minecraft/server/EntityItemFrame.java -index 16c8f2cdf7120337bec6f1d28822f3219c7d2d8f..19460d885682b1f182dcfbe13e11dae3fd98248d 100644 +index 661db537f6ac2b5a80e2e40a25966a2e3039199d..a43a42aa696994a17dda790d01d09e0abdaade00 100644 --- a/src/main/java/net/minecraft/server/EntityItemFrame.java +++ b/src/main/java/net/minecraft/server/EntityItemFrame.java @@ -199,7 +199,13 @@ public class EntityItemFrame extends EntityHanging { diff --git a/patches/server/0146-Add-boat-fall-damage-config.patch b/patches/server/0146-Add-boat-fall-damage-config.patch index 90b8e6117..f75ca3b7b 100644 --- a/patches/server/0146-Add-boat-fall-damage-config.patch +++ b/patches/server/0146-Add-boat-fall-damage-config.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add boat fall damage config diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 988e8545d4f9bf0fb0b8b8b482a29775b8a5c9f1..59f24ab35da5e3eb4d71dc9b3445abd6a2536266 100644 +index 3906805c5d6b9203e1ad160207154493660c8188..50cd947d0e47e7897a071fdf8ff6e5834e137d58 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java -@@ -1019,7 +1019,16 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -1030,7 +1030,16 @@ public class EntityPlayer extends EntityHuman implements ICrafting { if (this.isInvulnerable(damagesource)) { return false; } else { diff --git a/patches/server/0149-PaperPR-Apply-advancements-async.patch b/patches/server/0149-PaperPR-Apply-advancements-async.patch index 643f68aa1..59494e151 100644 --- a/patches/server/0149-PaperPR-Apply-advancements-async.patch +++ b/patches/server/0149-PaperPR-Apply-advancements-async.patch @@ -51,7 +51,7 @@ index 98c3884f478aed1eba82c15f258ebfa1c7203b14..244b58a7c41d2b0ba3117550282e68a1 } catch (ParseException parseexception) { throw new JsonSyntaxException("Invalid datetime: " + s, parseexception); diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 59f24ab35da5e3eb4d71dc9b3445abd6a2536266..e7fc241d879ec96862d9a82d784538f93e559051 100644 +index 50cd947d0e47e7897a071fdf8ff6e5834e137d58..5e4cc5e8f71c1c657041159305ce3e4a9aa9f46b 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -51,7 +51,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -83,7 +83,7 @@ index 59f24ab35da5e3eb4d71dc9b3445abd6a2536266..e7fc241d879ec96862d9a82d784538f9 // Purpur start if (this.world.purpurConfig.useNightVisionWhenRiding && this.getVehicle() != null && this.getVehicle().getRider() == this && world.getTime() % 100 == 0) { // 5 seconds -@@ -2123,7 +2125,26 @@ public class EntityPlayer extends EntityHuman implements ICrafting { +@@ -2134,7 +2136,26 @@ public class EntityPlayer extends EntityHuman implements ICrafting { this.worldChangeInvuln = false; } diff --git a/patches/server/0153-Lobotomize-stuck-villagers.patch b/patches/server/0153-Lobotomize-stuck-villagers.patch index 9bb50326b..4b0bfa02d 100644 --- a/patches/server/0153-Lobotomize-stuck-villagers.patch +++ b/patches/server/0153-Lobotomize-stuck-villagers.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Lobotomize stuck villagers diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 28c502b5721bd150d9b9d2d84cb07c55af8373f1..98e8f8d7751ca7489c7cb4d41e4f628dec7b5c5d 100644 +index b882c049870e461472fe3252922f206c6045dbff..073be230a8c0e2ccf6b4304e2988c270749f1a35 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -114,7 +114,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke diff --git a/patches/server/0157-Configurable-chance-for-wolves-to-spawn-rabid.patch b/patches/server/0157-Configurable-chance-for-wolves-to-spawn-rabid.patch index 5712822f8..9ab12e246 100644 --- a/patches/server/0157-Configurable-chance-for-wolves-to-spawn-rabid.patch +++ b/patches/server/0157-Configurable-chance-for-wolves-to-spawn-rabid.patch @@ -7,7 +7,7 @@ Configurable chance to spawn a wolf that is rabid. Rabid wolves attack all players, mobs, and animals. diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 418744b166571973a02dd6a31f0cc08376980076..ac14c0455cc92ea9f6af1d5451062ecab9980c69 100644 +index 81e362c6e0b9e6dde5df3f9009ecc8fdc1943789..60216266e9b18871b7b00be55805bce191e2105b 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -2094,6 +2094,7 @@ public abstract class EntityLiving extends Entity {