From b4d7562b87ad511b4be11cd5bafabd4ca9d103ac Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Mon, 11 Jan 2021 19:29:16 +0000 Subject: [PATCH] Looks like automagical relighting (#838) Fixes #686 --- .../com/boydti/fawe/bukkit/FaweBukkit.java | 10 ++ .../fawe/bukkit/adapter/BukkitGetBlocks.java | 7 + .../fawe/bukkit/adapter/NMSAdapter.java | 13 +- .../mc1_15_2/BukkitGetBlocks_1_15_2.java | 101 +++++++++---- .../mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java | 12 ++ .../mc1_16_1/BukkitGetBlocks_1_16_1.java | 101 +++++++++---- .../mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java | 12 ++ .../mc1_16_2/BukkitGetBlocks_1_16_2.java | 101 +++++++++---- .../mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java | 12 ++ .../mc1_16_4/BukkitGetBlocks_1_16_4.java | 101 +++++++++---- .../mc1_16_4/BukkitGetBlocks_1_16_4_Copy.java | 12 ++ .../boydti/fawe/FAWEPlatformAdapterImpl.java | 9 ++ .../main/java/com/boydti/fawe/FaweAPI.java | 2 +- .../src/main/java/com/boydti/fawe/IFawe.java | 2 + .../com/boydti/fawe/beta/CombinedBlocks.java | 6 + .../com/boydti/fawe/beta/IBatchProcessor.java | 8 + .../java/com/boydti/fawe/beta/IBlocks.java | 2 + .../java/com/boydti/fawe/beta/IChunkGet.java | 6 + .../java/com/boydti/fawe/beta/IChunkSet.java | 2 - .../blocks/FallbackChunkGet.java | 20 +++ .../implementation/blocks/NullChunkGet.java | 12 ++ .../implementation/chunk/ChunkHolder.java | 138 +++++++++++++++++- .../beta/implementation/chunk/NullChunk.java | 9 ++ .../implementation/lighting/NMSRelighter.java | 72 ++++++++- .../lighting/NullRelighter.java | 7 + .../lighting/RelightProcessor.java | 62 ++++++++ .../implementation/lighting/Relighter.java | 4 + .../processors/BatchProcessorHolder.java | 5 + .../processors/ChunkSendProcessor.java | 5 + .../processors/EmptyBatchProcessor.java | 5 + .../processors/MultiBatchProcessor.java | 71 +++++++-- .../processors/NullProcessor.java | 5 + .../processors/ProcessorScope.java | 40 +++++ .../queue/SingleThreadQueueExtent.java | 6 + .../com/boydti/fawe/jnbt/anvil/MCAChunk.java | 9 ++ .../object/changeset/AbstractChangeSet.java | 6 + .../fawe/object/extent/FaweRegionExtent.java | 6 + .../boydti/fawe/object/extent/NullExtent.java | 6 + .../com/boydti/fawe/regions/FaweMask.java | 6 + .../boydti/fawe/util/EditSessionBuilder.java | 18 ++- .../java/com/sk89q/worldedit/EditSession.java | 24 ++- .../com/sk89q/worldedit/extent/Extent.java | 4 + .../sk89q/worldedit/extent/MaskingExtent.java | 6 + .../sk89q/worldedit/regions/CuboidRegion.java | 2 +- .../com/sk89q/worldedit/regions/Region.java | 6 + .../src/main/resources/lang/strings.json | 1 + 46 files changed, 932 insertions(+), 142 deletions(-) create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/BukkitGetBlocks.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/FAWEPlatformAdapterImpl.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/RelightProcessor.java create mode 100644 worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ProcessorScope.java diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 6a3311e65..be3473b80 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -1,11 +1,13 @@ package com.boydti.fawe.bukkit; +import com.boydti.fawe.FAWEPlatformAdapterImpl; import com.boydti.fawe.Fawe; import com.boydti.fawe.IFawe; import com.boydti.fawe.beta.implementation.cache.preloader.AsyncPreloader; import com.boydti.fawe.beta.implementation.cache.preloader.Preloader; import com.boydti.fawe.beta.implementation.queue.QueueHandler; import com.boydti.fawe.bukkit.adapter.BukkitQueueHandler; +import com.boydti.fawe.bukkit.adapter.NMSAdapter; import com.boydti.fawe.bukkit.listener.BrushListener; import com.boydti.fawe.bukkit.listener.BukkitImageListener; import com.boydti.fawe.bukkit.listener.CFIPacketListener; @@ -58,6 +60,7 @@ public class FaweBukkit implements IFawe, Listener { private BukkitImageListener imageListener; private CFIPacketListener packetListener; private final boolean chunksStretched; + private final FAWEPlatformAdapterImpl platformAdapter; public FaweBukkit(Plugin plugin) { this.plugin = plugin; @@ -81,6 +84,8 @@ public class FaweBukkit implements IFawe, Listener { chunksStretched = Integer.parseInt(Bukkit.getBukkitVersion().split("-")[0].split("\\.")[1]) >= 16; + platformAdapter = new NMSAdapter(); + //PlotSquared support is limited to Spigot/Paper as of 02/20/2020 TaskManager.IMP.later(this::setupPlotSquared, 0); @@ -294,6 +299,11 @@ public class FaweBukkit implements IFawe, Listener { return chunksStretched; } + @Override + public FAWEPlatformAdapterImpl getPlatformAdapter() { + return platformAdapter; + } + private void setupPlotSquared() { Plugin plotSquared = this.plugin.getServer().getPluginManager().getPlugin("PlotSquared"); if (plotSquared == null) { diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/BukkitGetBlocks.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/BukkitGetBlocks.java new file mode 100644 index 000000000..c06466cb7 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/BukkitGetBlocks.java @@ -0,0 +1,7 @@ +package com.boydti.fawe.bukkit.adapter; + +public interface BukkitGetBlocks { + + void send(int mask, boolean lighting); + +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/NMSAdapter.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/NMSAdapter.java index 0160fdd51..41c96f295 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/NMSAdapter.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/NMSAdapter.java @@ -1,5 +1,8 @@ package com.boydti.fawe.bukkit.adapter; +import com.boydti.fawe.FAWEPlatformAdapterImpl; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.implementation.chunk.ChunkHolder; import com.boydti.fawe.config.Settings; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.math.BlockVector3; @@ -10,7 +13,7 @@ import com.sk89q.worldedit.world.block.BlockTypesCache; import java.util.Map; import java.util.function.Function; -public class NMSAdapter { +public class NMSAdapter implements FAWEPlatformAdapterImpl { public static int createPalette(int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy, int[] num_palette_buffer, char[] set, Map ticking_blocks, boolean fastmode) { int air = 0; @@ -182,4 +185,12 @@ public class NMSAdapter { num_palette_buffer[0] = num_palette; return air; } + + @Override + public void sendChunk(IChunkGet chunk, int mask, boolean lighting) { + if (!(chunk instanceof BukkitGetBlocks)) { + throw new IllegalArgumentException("(IChunkGet) chunk not of type BukkitGetBlocks"); + } + ((BukkitGetBlocks) chunk).send(mask, lighting); + } } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java index c4a9dfc9c..55080de5f 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2.java @@ -7,6 +7,7 @@ import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.beta.implementation.lighting.HeightMapType; import com.boydti.fawe.beta.implementation.queue.QueueHandler; +import com.boydti.fawe.bukkit.adapter.BukkitGetBlocks; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_15_2.nbt.LazyCompoundTag_1_15_2; import com.boydti.fawe.config.Settings; @@ -74,7 +75,7 @@ import javax.annotation.Nullable; import static org.slf4j.LoggerFactory.getLogger; -public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { +public class BukkitGetBlocks_1_15_2 extends CharGetBlocks implements BukkitGetBlocks { private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_15_2.class); @@ -90,6 +91,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { private boolean createCopy = false; private BukkitGetBlocks_1_15_2_Copy copy = null; private boolean forceLoadSections = true; + private boolean lightUpdate = false; public BukkitGetBlocks_1_15_2(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); @@ -120,6 +122,37 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { return copy; } + @Override + public void setLightingToGet(char[][] light) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, EnumSkyBlock.BLOCK); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setSkyLightingToGet(char[][] light) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, EnumSkyBlock.SKY); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) { + BitArray bitArray = new BitArray(9, 256); + bitArray.fromRaw(data); + nmsChunk.heightMap.get(HeightMap.Type.valueOf(type.name())).a(bitArray.getData()); + } + public int getChunkZ() { return chunkZ; } @@ -141,6 +174,34 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null; } + @Override + public void removeSectionLighting(int layer, boolean sky) { + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + if (nibble != null) { + lightUpdate = true; + synchronized (nibble) { + byte[] bytes = nibble.getCloneIfSet(); + if (bytes != NibbleArray.EMPTY_NIBBLE) { + Arrays.fill(bytes, (byte) 0); + } + } + } + if (sky) { + SectionPosition sectionPositionSky = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleSky = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPositionSky); + if (nibble != null) { + lightUpdate = true; + synchronized (nibbleSky) { + byte[] bytes = nibbleSky.getCloneIfSet(); + if (bytes != NibbleArray.EMPTY_NIBBLE) { + Arrays.fill(bytes, (byte) 0); + } + } + } + } + } + @Override public CompoundTag getTile(int x, int y, int z) { TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + ( @@ -436,33 +497,10 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { Map heightMaps = set.getHeightMaps(); for (Map.Entry entry : heightMaps.entrySet()) { - BitArray bitArray = new BitArray(9, 256); - bitArray.fromRaw(entry.getValue()); - nmsChunk.heightMap.get(HeightMap.Type.valueOf(entry.getKey().name())).a(bitArray.getData()); - } - - boolean lightUpdate = false; - - // Lighting - char[][] light = set.getLight(); - if (light != null) { - lightUpdate = true; - try { - fillLightNibble(light, EnumSkyBlock.BLOCK); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - char[][] skyLight = set.getSkyLight(); - if (skyLight != null) { - lightUpdate = true; - try { - fillLightNibble(skyLight, EnumSkyBlock.SKY); - } catch (Throwable e) { - e.printStackTrace(); - } + BukkitGetBlocks_1_15_2.this.setHeightmapToGet(entry.getKey(), entry.getValue()); } + BukkitGetBlocks_1_15_2.this.setLightingToGet(set.getLight()); + BukkitGetBlocks_1_15_2.this.setSkyLightingToGet(set.getSkyLight()); Runnable[] syncTasks = null; @@ -584,7 +622,9 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { nmsChunk.mustNotSave = false; nmsChunk.markDirty(); // send to player - BukkitAdapter_1_15_2.sendChunk(nmsWorld, chunkX, chunkZ, finalMask, finalLightUpdate); + if (Settings.IMP.LIGHTING.MODE == 0 || !Settings.IMP.LIGHTING.DELAY_PACKET_SENDING) { + this.send(finalMask, finalLightUpdate); + } if (finalizer != null) { finalizer.run(); } @@ -636,6 +676,11 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks { } } + @Override + public synchronized void send(int mask, boolean lighting) { + BukkitAdapter_1_15_2.sendChunk(world, chunkX, chunkZ, mask, lighting); + } + @Override public synchronized char[] update(int layer, char[] data) { ChunkSection section = getSections(true)[layer]; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java index 8fddf5db1..3a6aec938 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java @@ -104,6 +104,15 @@ public class BukkitGetBlocks_1_15_2_Copy implements IChunkGet { return false; } + @Override + public void setLightingToGet(char[][] lighting) {} + + @Override + public void setSkyLightingToGet(char[][] lighting) {} + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) {} + protected void storeBiomes(BiomeStorage biomeStorage) { this.biomeStorage = new BiomeStorage(BukkitAdapter_1_15_2.getBiomeArray(biomeStorage).clone()); } @@ -122,6 +131,9 @@ public class BukkitGetBlocks_1_15_2_Copy implements IChunkGet { return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null; } + @Override + public void removeSectionLighting(int layer, boolean sky) {} + @Override public boolean trim(boolean aggressive, int layer) { return false; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java index bf2ca9670..acef040e3 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1.java @@ -7,6 +7,7 @@ import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.beta.implementation.lighting.HeightMapType; import com.boydti.fawe.beta.implementation.queue.QueueHandler; +import com.boydti.fawe.bukkit.adapter.BukkitGetBlocks; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1; import com.boydti.fawe.config.Settings; @@ -74,7 +75,7 @@ import javax.annotation.Nullable; import static org.slf4j.LoggerFactory.getLogger; -public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { +public class BukkitGetBlocks_1_16_1 extends CharGetBlocks implements BukkitGetBlocks { private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_16_1.class); @@ -90,6 +91,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { private boolean createCopy = false; private BukkitGetBlocks_1_16_1_Copy copy = null; private boolean forceLoadSections = true; + private boolean lightUpdate = false; public BukkitGetBlocks_1_16_1(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); @@ -121,6 +123,37 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { return copy; } + @Override + public void setLightingToGet(char[][] light) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, EnumSkyBlock.BLOCK); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setSkyLightingToGet(char[][] light) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, EnumSkyBlock.SKY); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) { + BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256); + bitArray.fromRaw(data); + nmsChunk.heightMap.get(HeightMap.Type.valueOf(type.name())).a(bitArray.getData()); + } + public int getChunkZ() { return chunkZ; } @@ -142,6 +175,34 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null; } + @Override + public void removeSectionLighting(int layer, boolean sky) { + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + if (nibble != null) { + lightUpdate = true; + synchronized (nibble) { + byte[] bytes = nibble.getCloneIfSet(); + if (bytes != NibbleArray.EMPTY_NIBBLE) { + Arrays.fill(bytes, (byte) 0); + } + } + } + if (sky) { + SectionPosition sectionPositionSky = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleSky = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPositionSky); + if (nibble != null) { + lightUpdate = true; + synchronized (nibbleSky) { + byte[] bytes = nibbleSky.getCloneIfSet(); + if (bytes != NibbleArray.EMPTY_NIBBLE) { + Arrays.fill(bytes, (byte) 0); + } + } + } + } + } + @Override public CompoundTag getTile(int x, int y, int z) { TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + ( @@ -438,33 +499,10 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { Map heightMaps = set.getHeightMaps(); for (Map.Entry entry : heightMaps.entrySet()) { - BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256); - bitArray.fromRaw(entry.getValue()); - nmsChunk.heightMap.get(HeightMap.Type.valueOf(entry.getKey().name())).a(bitArray.getData()); - } - - boolean lightUpdate = false; - - // Lighting - char[][] light = set.getLight(); - if (light != null) { - lightUpdate = true; - try { - fillLightNibble(light, EnumSkyBlock.BLOCK); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - char[][] skyLight = set.getSkyLight(); - if (skyLight != null) { - lightUpdate = true; - try { - fillLightNibble(skyLight, EnumSkyBlock.SKY); - } catch (Throwable e) { - e.printStackTrace(); - } + BukkitGetBlocks_1_16_1.this.setHeightmapToGet(entry.getKey(), entry.getValue()); } + BukkitGetBlocks_1_16_1.this.setLightingToGet(set.getLight()); + BukkitGetBlocks_1_16_1.this.setSkyLightingToGet(set.getSkyLight()); Runnable[] syncTasks = null; @@ -586,7 +624,9 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { nmsChunk.mustNotSave = false; nmsChunk.markDirty(); // send to player - BukkitAdapter_1_16_1.sendChunk(nmsWorld, chunkX, chunkZ, finalMask, finalLightUpdate); + if (Settings.IMP.LIGHTING.MODE == 0 || !Settings.IMP.LIGHTING.DELAY_PACKET_SENDING) { + this.send(finalMask, finalLightUpdate); + } if (finalizer != null) { finalizer.run(); } @@ -638,6 +678,11 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks { } } + @Override + public synchronized void send(int mask, boolean lighting) { + BukkitAdapter_1_16_1.sendChunk(world, chunkX, chunkZ, mask, lighting); + } + @Override public synchronized char[] update(int layer, char[] data) { ChunkSection section = getSections(true)[layer]; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java index 634d2c5ff..b2f1037d0 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java @@ -104,6 +104,15 @@ public class BukkitGetBlocks_1_16_1_Copy implements IChunkGet { return false; } + @Override + public void setLightingToGet(char[][] lighting) {} + + @Override + public void setSkyLightingToGet(char[][] lighting) {} + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) {} + protected void storeBiomes(BiomeStorage biomeStorage) { this.biomeStorage = new BiomeStorage(BukkitAdapter_1_16_1.getBiomeArray(biomeStorage).clone()); } @@ -122,6 +131,9 @@ public class BukkitGetBlocks_1_16_1_Copy implements IChunkGet { return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null; } + @Override + public void removeSectionLighting(int layer, boolean sky) {} + @Override public boolean trim(boolean aggressive, int layer) { return false; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java index e5c0dc447..94d8e2b15 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2.java @@ -7,6 +7,7 @@ import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.beta.implementation.lighting.HeightMapType; import com.boydti.fawe.beta.implementation.queue.QueueHandler; +import com.boydti.fawe.bukkit.adapter.BukkitGetBlocks; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_16_2.nbt.LazyCompoundTag_1_16_2; import com.boydti.fawe.config.Settings; @@ -75,7 +76,7 @@ import java.util.function.Function; import static org.slf4j.LoggerFactory.getLogger; -public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { +public class BukkitGetBlocks_1_16_2 extends CharGetBlocks implements BukkitGetBlocks { private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_16_2.class); @@ -91,6 +92,7 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { private boolean createCopy = false; private BukkitGetBlocks_1_16_2_Copy copy = null; private boolean forceLoadSections = true; + private boolean lightUpdate = false; public BukkitGetBlocks_1_16_2(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); @@ -121,6 +123,37 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { return copy; } + @Override + public void setLightingToGet(char[][] light) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, EnumSkyBlock.BLOCK); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setSkyLightingToGet(char[][] light) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, EnumSkyBlock.SKY); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) { + BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256); + bitArray.fromRaw(data); + nmsChunk.heightMap.get(HeightMap.Type.valueOf(type.name())).a(bitArray.getData()); + } + public int getChunkZ() { return chunkZ; } @@ -142,6 +175,34 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null; } + @Override + public void removeSectionLighting(int layer, boolean sky) { + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + if (nibble != null) { + lightUpdate = true; + synchronized (nibble) { + byte[] bytes = nibble.getCloneIfSet(); + if (bytes != NibbleArray.EMPTY_NIBBLE) { + Arrays.fill(bytes, (byte) 0); + } + } + } + if (sky) { + SectionPosition sectionPositionSky = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleSky = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPositionSky); + if (nibble != null) { + lightUpdate = true; + synchronized (nibbleSky) { + byte[] bytes = nibbleSky.getCloneIfSet(); + if (bytes != NibbleArray.EMPTY_NIBBLE) { + Arrays.fill(bytes, (byte) 0); + } + } + } + } + } + @Override public CompoundTag getTile(int x, int y, int z) { TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + ( @@ -441,33 +502,10 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { Map heightMaps = set.getHeightMaps(); for (Map.Entry entry : heightMaps.entrySet()) { - BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256); - bitArray.fromRaw(entry.getValue()); - nmsChunk.heightMap.get(HeightMap.Type.valueOf(entry.getKey().name())).a(bitArray.getData()); - } - - boolean lightUpdate = false; - - // Lighting - char[][] light = set.getLight(); - if (light != null) { - lightUpdate = true; - try { - fillLightNibble(light, EnumSkyBlock.BLOCK); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - char[][] skyLight = set.getSkyLight(); - if (skyLight != null) { - lightUpdate = true; - try { - fillLightNibble(skyLight, EnumSkyBlock.SKY); - } catch (Throwable e) { - e.printStackTrace(); - } + BukkitGetBlocks_1_16_2.this.setHeightmapToGet(entry.getKey(), entry.getValue()); } + BukkitGetBlocks_1_16_2.this.setLightingToGet(set.getLight()); + BukkitGetBlocks_1_16_2.this.setSkyLightingToGet(set.getSkyLight()); Runnable[] syncTasks = null; @@ -589,7 +627,9 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { nmsChunk.mustNotSave = false; nmsChunk.markDirty(); // send to player - BukkitAdapter_1_16_2.sendChunk(nmsWorld, chunkX, chunkZ, finalMask, finalLightUpdate); + if (Settings.IMP.LIGHTING.MODE == 0 || !Settings.IMP.LIGHTING.DELAY_PACKET_SENDING) { + this.send(finalMask, finalLightUpdate); + } if (finalizer != null) { finalizer.run(); } @@ -641,6 +681,11 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks { } } + @Override + public synchronized void send(int mask, boolean lighting) { + BukkitAdapter_1_16_2.sendChunk(world, chunkX, chunkZ, mask, lighting); + } + @Override public synchronized char[] update(int layer, char[] data) { ChunkSection section = getSections(true)[layer]; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java index bb772de71..688a492bb 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java @@ -105,6 +105,15 @@ public class BukkitGetBlocks_1_16_2_Copy implements IChunkGet { return false; } + @Override + public void setLightingToGet(char[][] lighting) {} + + @Override + public void setSkyLightingToGet(char[][] lighting) {} + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) {} + protected void storeBiomes(BiomeStorage biomeStorage) { this.biomeStorage = new BiomeStorage(biomeStorage.g, BukkitAdapter_1_16_2.getBiomeArray(biomeStorage).clone()); } @@ -123,6 +132,9 @@ public class BukkitGetBlocks_1_16_2_Copy implements IChunkGet { return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null; } + @Override + public void removeSectionLighting(int layer, boolean sky) {} + @Override public boolean trim(boolean aggressive, int layer) { return false; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_4/BukkitGetBlocks_1_16_4.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_4/BukkitGetBlocks_1_16_4.java index d07e4b8f2..37a26a089 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_4/BukkitGetBlocks_1_16_4.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_4/BukkitGetBlocks_1_16_4.java @@ -7,6 +7,7 @@ import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; import com.boydti.fawe.beta.implementation.lighting.HeightMapType; import com.boydti.fawe.beta.implementation.queue.QueueHandler; +import com.boydti.fawe.bukkit.adapter.BukkitGetBlocks; import com.boydti.fawe.bukkit.adapter.DelegateLock; import com.boydti.fawe.bukkit.adapter.mc1_16_4.nbt.LazyCompoundTag_1_16_4; import com.boydti.fawe.config.Settings; @@ -75,7 +76,7 @@ import java.util.function.Function; import static org.slf4j.LoggerFactory.getLogger; -public class BukkitGetBlocks_1_16_4 extends CharGetBlocks { +public class BukkitGetBlocks_1_16_4 extends CharGetBlocks implements BukkitGetBlocks { private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_16_4.class); @@ -91,6 +92,7 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks { private boolean createCopy = false; private BukkitGetBlocks_1_16_4_Copy copy = null; private boolean forceLoadSections = true; + private boolean lightUpdate = false; public BukkitGetBlocks_1_16_4(World world, int chunkX, int chunkZ) { this(((CraftWorld) world).getHandle(), chunkX, chunkZ); @@ -121,6 +123,37 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks { return copy; } + @Override + public void setLightingToGet(char[][] light) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, EnumSkyBlock.BLOCK); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setSkyLightingToGet(char[][] light) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, EnumSkyBlock.SKY); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) { + BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256); + bitArray.fromRaw(data); + nmsChunk.heightMap.get(HeightMap.Type.valueOf(type.name())).a(bitArray.getData()); + } + public int getChunkZ() { return chunkZ; } @@ -142,6 +175,34 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks { return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null; } + @Override + public void removeSectionLighting(int layer, boolean sky) { + SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibble = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition); + if (nibble != null) { + lightUpdate = true; + synchronized (nibble) { + byte[] bytes = nibble.getCloneIfSet(); + if (bytes != NibbleArray.EMPTY_NIBBLE) { + Arrays.fill(bytes, (byte) 0); + } + } + } + if (sky) { + SectionPosition sectionPositionSky = SectionPosition.a(nmsChunk.getPos(), layer); + NibbleArray nibbleSky = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPositionSky); + if (nibble != null) { + lightUpdate = true; + synchronized (nibbleSky) { + byte[] bytes = nibbleSky.getCloneIfSet(); + if (bytes != NibbleArray.EMPTY_NIBBLE) { + Arrays.fill(bytes, (byte) 0); + } + } + } + } + } + @Override public CompoundTag getTile(int x, int y, int z) { TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + ( @@ -441,33 +502,10 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks { Map heightMaps = set.getHeightMaps(); for (Map.Entry entry : heightMaps.entrySet()) { - BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256); - bitArray.fromRaw(entry.getValue()); - nmsChunk.heightMap.get(HeightMap.Type.valueOf(entry.getKey().name())).a(bitArray.getData()); - } - - boolean lightUpdate = false; - - // Lighting - char[][] light = set.getLight(); - if (light != null) { - lightUpdate = true; - try { - fillLightNibble(light, EnumSkyBlock.BLOCK); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - char[][] skyLight = set.getSkyLight(); - if (skyLight != null) { - lightUpdate = true; - try { - fillLightNibble(skyLight, EnumSkyBlock.SKY); - } catch (Throwable e) { - e.printStackTrace(); - } + BukkitGetBlocks_1_16_4.this.setHeightmapToGet(entry.getKey(), entry.getValue()); } + BukkitGetBlocks_1_16_4.this.setLightingToGet(set.getLight()); + BukkitGetBlocks_1_16_4.this.setSkyLightingToGet(set.getSkyLight()); Runnable[] syncTasks = null; @@ -589,7 +627,9 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks { nmsChunk.mustNotSave = false; nmsChunk.markDirty(); // send to player - BukkitAdapter_1_16_4.sendChunk(nmsWorld, chunkX, chunkZ, finalMask, finalLightUpdate); + if (Settings.IMP.LIGHTING.MODE == 0 || !Settings.IMP.LIGHTING.DELAY_PACKET_SENDING) { + this.send(finalMask, finalLightUpdate); + } if (finalizer != null) { finalizer.run(); } @@ -641,6 +681,11 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks { } } + @Override + public synchronized void send(int mask, boolean lighting) { + BukkitAdapter_1_16_4.sendChunk(world, chunkX, chunkZ, mask, lighting); + } + @Override public synchronized char[] update(int layer, char[] data) { ChunkSection section = getSections(true)[layer]; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_4/BukkitGetBlocks_1_16_4_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_4/BukkitGetBlocks_1_16_4_Copy.java index 3f1db2730..02149eaaa 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_4/BukkitGetBlocks_1_16_4_Copy.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_4/BukkitGetBlocks_1_16_4_Copy.java @@ -105,6 +105,15 @@ public class BukkitGetBlocks_1_16_4_Copy implements IChunkGet { return false; } + @Override + public void setLightingToGet(char[][] lighting) {} + + @Override + public void setSkyLightingToGet(char[][] lighting) {} + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) {} + protected void storeBiomes(BiomeStorage biomeStorage) { this.biomeStorage = new BiomeStorage(biomeStorage.g, BukkitAdapter_1_16_4.getBiomeArray(biomeStorage).clone()); } @@ -123,6 +132,9 @@ public class BukkitGetBlocks_1_16_4_Copy implements IChunkGet { return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null; } + @Override + public void removeSectionLighting(int layer, boolean sky) {} + @Override public boolean trim(boolean aggressive, int layer) { return false; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FAWEPlatformAdapterImpl.java b/worldedit-core/src/main/java/com/boydti/fawe/FAWEPlatformAdapterImpl.java new file mode 100644 index 000000000..068f03680 --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/FAWEPlatformAdapterImpl.java @@ -0,0 +1,9 @@ +package com.boydti.fawe; + +import com.boydti.fawe.beta.IChunkGet; + +public interface FAWEPlatformAdapterImpl { + + void sendChunk(IChunkGet chunk, int mask, boolean lighting); + +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java b/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java index aa8ef4859..30dedb3f0 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java @@ -361,7 +361,7 @@ public class FaweAPI { } else { relighter.removeLighting(); } - relighter.sendChunks(); + relighter.flush(); return count; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java index 1ff8a7566..4859ed6d3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/IFawe.java @@ -43,4 +43,6 @@ public interface IFawe { return true; } + FAWEPlatformAdapterImpl getPlatformAdapter(); + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java index 61917bea1..d03a3e9ec 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java @@ -39,6 +39,12 @@ public class CombinedBlocks implements IBlocks { return bitMask; } + @Override + public void removeSectionLighting(int layer, boolean sky) { + primary.removeSectionLighting(layer, sky); + secondary.removeSectionLighting(layer, sky); + } + @Override public boolean hasSection(int layer) { return primary.hasSection(layer) || secondary.hasSection(layer); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java index 1159000d6..802bec530 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBatchProcessor.java @@ -3,6 +3,7 @@ package com.boydti.fawe.beta; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor; import com.boydti.fawe.beta.implementation.processors.MultiBatchProcessor; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; @@ -126,4 +127,11 @@ public interface IBatchProcessor { } return this; } + + /** + * Default to CUSTOM ProcessorScope as we want custom processors people add to be before we write history, but after FAWE does it's own stuff. + */ + default ProcessorScope getScope() { + return ProcessorScope.CUSTOM; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java index eda96ee39..cf05147cf 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java @@ -42,6 +42,8 @@ public interface IBlocks extends Trimable { .map(layer -> (1 << layer)).sum(); } + void removeSectionLighting(int layer, boolean sky); + boolean trim(boolean aggressive, int layer); IBlocks reset(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java index 887006799..fb7b25db9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java @@ -56,4 +56,10 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput { default IChunkGet getCopy() { return null; } + + void setLightingToGet(char[][] lighting); + + void setSkyLightingToGet(char[][] lighting); + + void setHeightmapToGet(HeightMapType type, int[] data); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java index 73ed38ce8..c550a30c9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java @@ -50,8 +50,6 @@ public interface IChunkSet extends IBlocks, OutputExtent { void setSkyLightLayer(int layer, char[] toSet); - void removeSectionLighting(int layer, boolean sky); - void setFullBright(int layer); void setEntity(CompoundTag tag); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java index 0dd9ab884..686c2f1b6 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java @@ -44,6 +44,11 @@ public class FallbackChunkGet implements IChunkGet { return extent.getBiomeType(bx + x, y, bz + z); } + @Override + public void removeSectionLighting(int layer, boolean sky) { + // do nothing + } + @Override public BlockState getBlock(int x, int y, int z) { return extent.getBlock(bx + x, y, bz + z); @@ -106,6 +111,21 @@ public class FallbackChunkGet implements IChunkGet { return false; } + @Override + public void setLightingToGet(char[][] lighting) { + // do nothing + } + + @Override + public void setSkyLightingToGet(char[][] lighting) { + // do nothing + } + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) { + // do nothing + } + @Override public boolean trim(boolean aggressive) { return true; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java index a59b31b40..00f522c56 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java @@ -38,6 +38,9 @@ public final class NullChunkGet implements IChunkGet { return BiomeTypes.FOREST; } + @Override + public void removeSectionLighting(int layer, boolean sky) {} + @NotNull public BlockState getBlock(int x, int y, int z) { return BlockTypes.AIR.getDefaultState(); @@ -69,6 +72,15 @@ public final class NullChunkGet implements IChunkGet { return false; } + @Override + public void setLightingToGet(char[][] lighting) {} + + @Override + public void setSkyLightingToGet(char[][] lighting) {} + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) {} + public boolean trim(boolean aggressive) { return true; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java index 9c4122fac..c6380b549 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java @@ -151,6 +151,25 @@ public class ChunkHolder> implements IQueueChunk { return createCopy; } + @Override + public void setLightingToGet(char[][] lighting) { + delegate.setLightingToGet(this, lighting); + } + + @Override + public void setSkyLightingToGet(char[][] lighting) { + delegate.setSkyLightingToGet(this, lighting); + } + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) { + delegate.setHeightmapToGet(this, type, data); + } + + public void flushLightToGet(boolean heightmaps) { + delegate.flushLightToGet(this, heightmaps); + } + private static final IBlockDelegate BOTH = new IBlockDelegate() { @Override public IChunkGet get(ChunkHolder chunk) { @@ -187,6 +206,7 @@ public class ChunkHolder> implements IQueueChunk { @Override public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) { chunk.chunkSet.removeSectionLighting(layer, sky); + chunk.chunkExisting.removeSectionLighting(layer, sky); } @Override @@ -264,6 +284,27 @@ public class ChunkHolder> implements IQueueChunk { @Override public int[] getHeightMap(ChunkHolder chunk, HeightMapType type) { return chunk.chunkExisting.getHeightMap(type); } + + @Override + public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) { + chunk.chunkExisting.setLightingToGet(chunk.chunkSet.getLight()); + chunk.chunkExisting.setSkyLightingToGet(chunk.chunkSet.getSkyLight()); + } + + @Override + public void setLightingToGet(ChunkHolder chunk, char[][] lighting) { + chunk.chunkExisting.setLightingToGet(lighting); + } + + @Override + public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) { + chunk.chunkExisting.setSkyLightingToGet(lighting); + } + + @Override + public void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data) { + chunk.chunkExisting.setHeightmapToGet(type, data); + } }; private static final IBlockDelegate GET = new IBlockDelegate() { @@ -382,6 +423,26 @@ public class ChunkHolder> implements IQueueChunk { @Override public int[] getHeightMap(ChunkHolder chunk, HeightMapType type) { return chunk.chunkExisting.getHeightMap(type); } + + @Override + public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) { + // Do nothing as no lighting to flush to GET + } + + @Override + public void setLightingToGet(ChunkHolder chunk, char[][] lighting) { + chunk.chunkExisting.setLightingToGet(lighting); + } + + @Override + public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) { + chunk.chunkExisting.setSkyLightingToGet(lighting); + } + + @Override + public void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data) { + chunk.chunkExisting.setHeightmapToGet(type, data); + } }; private static final IBlockDelegate SET = new IBlockDelegate() { @@ -421,7 +482,9 @@ public class ChunkHolder> implements IQueueChunk { @Override public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) { - chunk.chunkSet.removeSectionLighting(layer, sky); + chunk.getOrCreateGet(); + chunk.delegate = BOTH; + chunk.removeSectionLighting(layer, sky); } @Override @@ -524,6 +587,39 @@ public class ChunkHolder> implements IQueueChunk { chunk.chunkExisting.trim(false); return chunk.getHeightMap(type); } + + @Override + public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) { + chunk.getOrCreateGet(); + chunk.delegate = BOTH; + chunk.chunkExisting.trim(false); + chunk.chunkExisting.setLightingToGet(chunk.chunkSet.getLight()); + chunk.chunkExisting.setSkyLightingToGet(chunk.chunkSet.getSkyLight()); + } + + @Override + public void setLightingToGet(ChunkHolder chunk, char[][] lighting) { + chunk.getOrCreateGet(); + chunk.delegate = BOTH; + chunk.chunkExisting.trim(false); + chunk.chunkExisting.setLightingToGet(lighting); + } + + @Override + public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) { + chunk.getOrCreateGet(); + chunk.delegate = BOTH; + chunk.chunkExisting.trim(false); + chunk.chunkExisting.setSkyLightingToGet(lighting); + } + + @Override + public void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data) { + chunk.getOrCreateGet(); + chunk.delegate = BOTH; + chunk.chunkExisting.trim(false); + chunk.chunkExisting.setHeightmapToGet(type, data); + } }; private static final IBlockDelegate NULL = new IBlockDelegate() { @@ -597,8 +693,9 @@ public class ChunkHolder> implements IQueueChunk { @Override public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) { + chunk.getOrCreateGet(); chunk.getOrCreateSet(); - chunk.delegate = SET; + chunk.delegate = BOTH; chunk.removeSectionLighting(layer, sky); } @@ -667,6 +764,35 @@ public class ChunkHolder> implements IQueueChunk { chunk.chunkExisting.trim(false); return chunk.getHeightMap(type); } + + @Override + public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) { + // Do nothing as no light to flush + } + + @Override + public void setLightingToGet(ChunkHolder chunk, char[][] lighting) { + chunk.getOrCreateGet(); + chunk.delegate = GET; + chunk.chunkExisting.trim(false); + chunk.setLightingToGet(lighting); + } + + @Override + public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) { + chunk.getOrCreateGet(); + chunk.delegate = GET; + chunk.chunkExisting.trim(false); + chunk.setSkyLightingToGet(lighting); + } + + @Override + public void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data) { + chunk.getOrCreateGet(); + chunk.delegate = GET; + chunk.chunkExisting.trim(false); + chunk.setHeightmapToGet(type, data); + } }; @Override @@ -947,5 +1073,13 @@ public class ChunkHolder> implements IQueueChunk { int getOpacity(ChunkHolder chunk, int x, int y, int z); int[] getHeightMap(ChunkHolder chunk, HeightMapType type); + + void flushLightToGet(ChunkHolder chunk, boolean heightmaps); + + void setLightingToGet(ChunkHolder chunk, char[][] lighting); + + void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting); + + void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java index 35c99c17f..4bfce286a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java @@ -177,6 +177,15 @@ public final class NullChunk implements IQueueChunk { return false; } + @Override + public void setLightingToGet(char[][] lighting) {} + + @Override + public void setSkyLightingToGet(char[][] lighting) {} + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) {} + @Nullable public > T call(@Nullable IChunkSet set, @Nullable Runnable finalize) { return null; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NMSRelighter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NMSRelighter.java index b10281e29..cccda08c3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NMSRelighter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NMSRelighter.java @@ -1,9 +1,11 @@ package com.boydti.fawe.beta.implementation.lighting; +import com.boydti.fawe.Fawe; import com.boydti.fawe.beta.IQueueChunk; import com.boydti.fawe.beta.IQueueExtent; import com.boydti.fawe.beta.implementation.chunk.ChunkHolder; import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.RelightMode; import com.boydti.fawe.object.RunnableVal; import com.boydti.fawe.object.collection.BlockVectorSet; import com.boydti.fawe.util.MathMan; @@ -34,6 +36,7 @@ import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; public class NMSRelighter implements Relighter { @@ -64,11 +67,17 @@ public class NMSRelighter implements Relighter { private final Map lightQueue; private final AtomicBoolean lightLock = new AtomicBoolean(false); private final ConcurrentHashMap concurrentLightQueue; + private final RelightMode relightMode; private final int maxY; private final boolean calculateHeightMaps; + private final ReentrantLock lightingLock; private boolean removeFirst; public NMSRelighter(IQueueExtent queue, boolean calculateHeightMaps) { + this(queue, calculateHeightMaps, null); + } + + public NMSRelighter(IQueueExtent queue, boolean calculateHeightMaps, RelightMode relightMode) { this.queue = queue; this.skyToRelight = new Long2ObjectOpenHashMap<>(12); this.lightQueue = new Long2ObjectOpenHashMap<>(12); @@ -77,12 +86,19 @@ public class NMSRelighter implements Relighter { this.heightMaps = new Long2ObjectOpenHashMap<>(12); this.maxY = queue.getMaxY(); this.calculateHeightMaps = calculateHeightMaps; + this.relightMode = relightMode != null ? relightMode : RelightMode.valueOf(Settings.IMP.LIGHTING.MODE); + this.lightingLock = new ReentrantLock(); } @Override public boolean isEmpty() { return skyToRelight.isEmpty() && lightQueue.isEmpty() && extentdSkyToRelight.isEmpty() && concurrentLightQueue.isEmpty(); } + @Override + public synchronized ReentrantLock getLock() { + return lightingLock; + } + @Override public synchronized void removeAndRelight(boolean sky) { removeFirst = true; fixLightingSafe(sky); @@ -778,6 +794,7 @@ public class NMSRelighter implements Relighter { } } fixBlockLighting(); + sendChunks(); } catch (Throwable e) { e.printStackTrace(); } @@ -786,7 +803,11 @@ public class NMSRelighter implements Relighter { public void fixBlockLighting() { synchronized (lightQueue) { while (!lightLock.compareAndSet(false, true)) { - ; + try { + lightLock.wait(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } } try { updateBlockLight(this.lightQueue); @@ -796,7 +817,7 @@ public class NMSRelighter implements Relighter { } } - public synchronized void sendChunks() { + public synchronized void flush() { Iterator> iter = chunksToSend.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -827,6 +848,40 @@ public class NMSRelighter implements Relighter { } } + public synchronized void sendChunks() { + RunnableVal runnable = new RunnableVal() { + @Override + public void run(Object value) { + Iterator> iter = chunksToSend.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + long pair = entry.getKey(); + int bitMask = entry.getValue(); + int x = MathMan.unpairIntX(pair); + int z = MathMan.unpairIntY(pair); + ChunkHolder chunk = (ChunkHolder) queue.getOrCreateChunk(x, z); + chunk.setBitMask(bitMask); + if (calculateHeightMaps && heightMaps != null) { + Map heightMapList = heightMaps.get(pair); + if (heightMapList != null) { + for (Map.Entry heightMapEntry : heightMapList.entrySet()) { + chunk.setHeightMap(heightMapEntry.getKey(), heightMapEntry.getValue()); + } + } + } + chunk.flushLightToGet(true); + Fawe.imp().getPlatformAdapter().sendChunk(chunk.getOrCreateGet(), bitMask, true); + iter.remove(); + } + } + }; + if (Settings.IMP.LIGHTING.ASYNC) { + runnable.run(); + } else { + TaskManager.IMP.sync(runnable); + } + } + public synchronized void fixSkyLighting() { // Order chunks Map map = getSkyMap(); @@ -913,7 +968,7 @@ public class NMSRelighter implements Relighter { for (RelightSkyEntry chunk : chunks) { // Propagate skylight int layer = y >> 4; byte[] mask = chunk.mask; - if ((y & 15) == 15 && chunk.fix[layer] != SkipReason.NONE) { + if (chunk.fix[layer] != SkipReason.NONE) { if ((y & 15) == 0 && layer != 0 && chunk.fix[layer - 1] == SkipReason.NONE) { fill(mask, chunk.x, y, chunk.z, chunk.fix[layer]); } @@ -945,7 +1000,7 @@ public class NMSRelighter implements Relighter { BlockMaterial material = state.getMaterial(); int opacity = material.getLightOpacity(); int brightness = material.getLightValue(); - if (brightness > 1) { + if (brightness != iChunk.getEmmittedLight(x, y, z)) { addLightUpdate(bx + x, y, bz + z); } @@ -957,25 +1012,26 @@ public class NMSRelighter implements Relighter { if (heightMapList.get(HeightMapType.OCEAN_FLOOR)[j] == 0 && material.isSolid()) { heightMapList.get(HeightMapType.OCEAN_FLOOR)[j] = y + 1; } + Map, Object> states = state.getStates(); try { if (heightMapList.get(HeightMapType.MOTION_BLOCKING)[j] == 0 && (material.isSolid() || material.isLiquid() || ( - state.getStates().containsKey(waterLogged) && state.getState(waterLogged)))) { + states.containsKey(waterLogged) && state.getState(waterLogged)))) { heightMapList.get(HeightMapType.MOTION_BLOCKING)[j] = y + 1; } } catch (Exception ignored) { log.debug("Error calculating waterlogged state for BlockState: " + state.getBlockType().getId() + ". States:"); - log.debug(state.getStates().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()) + log.debug(states.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()) .collect(Collectors.joining(", ", "{", "}"))); } try { if (heightMapList.get(HeightMapType.MOTION_BLOCKING_NO_LEAVES)[j] == 0 && (material.isSolid() || material.isLiquid() || ( - state.getStates().containsKey(waterLogged) && state.getState(waterLogged))) && !state.getBlockType().getId() + states.containsKey(waterLogged) && state.getState(waterLogged))) && !state.getBlockType().getId() .toLowerCase().contains("leaves")) { heightMapList.get(HeightMapType.MOTION_BLOCKING_NO_LEAVES)[j] = y + 1; } } catch (Exception ignored) { log.debug("Error calculating waterlogged state for BlockState: " + state.getBlockType().getId() + ". States:"); - log.debug(state.getStates().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()) + log.debug(states.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()) .collect(Collectors.joining(", ", "{", "}"))); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NullRelighter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NullRelighter.java index 45de35d31..fe9eb28b3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NullRelighter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NullRelighter.java @@ -1,5 +1,7 @@ package com.boydti.fawe.beta.implementation.lighting; +import java.util.concurrent.locks.ReentrantLock; + public class NullRelighter implements Relighter { public static NullRelighter INSTANCE = new NullRelighter(); @@ -46,4 +48,9 @@ public class NullRelighter implements Relighter { public boolean isEmpty() { return true; } + + @Override + public ReentrantLock getLock() { + return null; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/RelightProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/RelightProcessor.java new file mode 100644 index 000000000..d19b535ef --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/RelightProcessor.java @@ -0,0 +1,62 @@ +package com.boydti.fawe.beta.implementation.lighting; + +import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.IChunk; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; +import com.boydti.fawe.config.Settings; +import com.sk89q.worldedit.extent.Extent; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +public class RelightProcessor implements IBatchProcessor { + + private final Relighter relighter; + + public RelightProcessor(Relighter relighter) { + if (relighter instanceof NullRelighter) { + throw new IllegalArgumentException("NullRelighter cannot be used for a RelightProcessor"); + } + this.relighter = relighter; + } + + @Override + public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { + if (Settings.IMP.LIGHTING.MODE == 2) { + relighter.addChunk(chunk.getX(), chunk.getZ(), null, chunk.getBitMask()); + } else if (Settings.IMP.LIGHTING.MODE == 1) { + byte[] fix = new byte[16]; + boolean relight = false; + for (int i = 15; i >= 0; i--) { + if (!set.hasSection(i)) { + fix[i] = Relighter.SkipReason.AIR; + continue; + } + relight = true; + break; + } + if (relight) { + relighter.addChunk(chunk.getX(), chunk.getZ(), fix, chunk.getBitMask()); + } + } + return set; + } + + @Override + public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { + return CompletableFuture.completedFuture(set); + } + + @Override + public @Nullable Extent construct(Extent child) { + throw new UnsupportedOperationException("Processing only"); + } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.READING_SET_BLOCKS; + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/Relighter.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/Relighter.java index ad60cfa94..99f809075 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/Relighter.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/Relighter.java @@ -1,5 +1,7 @@ package com.boydti.fawe.beta.implementation.lighting; +import java.util.concurrent.locks.ReentrantLock; + public interface Relighter { /** @@ -66,6 +68,8 @@ public interface Relighter { */ boolean isEmpty(); + ReentrantLock getLock(); + class SkipReason { public static final byte NONE = 0; public static final byte AIR = 1; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java index 17a700621..cf5c53473 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/BatchProcessorHolder.java @@ -51,4 +51,9 @@ public class BatchProcessorHolder implements IBatchProcessorHolder { IBatchProcessor tmp = getProcessor(); return super.toString() + "{" + (tmp == this ? "" : getProcessor()) + "}"; } + + @Override + public ProcessorScope getScope() { + return getProcessor().getScope(); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java index 8767430b6..bd15871fa 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ChunkSendProcessor.java @@ -81,4 +81,9 @@ public class ChunkSendProcessor implements IBatchProcessor { public Extent construct(Extent child) { throw new UnsupportedOperationException("Processing only"); } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.READING_SET_BLOCKS; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java index 6708d9d35..cccaa96a3 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/EmptyBatchProcessor.java @@ -49,4 +49,9 @@ public final class EmptyBatchProcessor implements IBatchProcessor { private EmptyBatchProcessor() { } + @Override + public ProcessorScope getScope() { + return ProcessorScope.ADDING_BLOCKS; + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java index 2108c7a76..a75b6e51c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/MultiBatchProcessor.java @@ -9,21 +9,30 @@ import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.util.StringMan; import com.google.common.cache.LoadingCache; import com.sk89q.worldedit.extent.Extent; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; -import java.util.Map; import java.util.function.Supplier; public class MultiBatchProcessor implements IBatchProcessor { + + private static final Logger log = LoggerFactory.getLogger(MultiBatchProcessor.class); + private IBatchProcessor[] processors; private final LoadingCache, Map> classToThreadIdToFilter = FaweCache.IMP.createCache((Supplier>) ConcurrentHashMap::new); - public MultiBatchProcessor(IBatchProcessor... processors) { this.processors = processors; } @@ -65,18 +74,31 @@ public class MultiBatchProcessor implements IBatchProcessor { @Override public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { + Map> ordered = new HashMap<>(); try { IChunkSet chunkSet = set; - for (int i = processors.length - 1; i >= 0; i--) { - IBatchProcessor processor = processors[i]; - if (processor instanceof Filter) { - chunkSet = ((IBatchProcessor) classToThreadIdToFilter.getUnchecked(processor.getClass()) - .computeIfAbsent(Thread.currentThread().getId(), k -> ((Filter) processor).fork())).processSet(chunk, get, chunkSet); - } else { - chunkSet = processor.processSet(chunk, get, chunkSet); + for (IBatchProcessor processor : processors) { + if (processor.getScope() != ProcessorScope.ADDING_BLOCKS) { + ordered.merge(processor.getScope().intValue(), new HashSet<>(Collections.singleton(processor)), (existing, theNew) -> { + existing.add(processor); + return existing; + }); + continue; } - if (chunkSet == null) { - return null; + chunkSet = processSet(processor, chunk, get, chunkSet); + } + if (ordered.size() > 0) { + for (int i = 1; i <= 4; i++) { + Set processors = ordered.get(i); + if (processors == null) { + continue; + } + for (IBatchProcessor processor : processors) { + chunkSet = processSet(processor,chunk, get, chunkSet); + if (chunkSet == null) { + return null; + } + } } } return chunkSet; @@ -86,11 +108,25 @@ public class MultiBatchProcessor implements IBatchProcessor { } } + @Nullable + private IChunkSet processSet(IBatchProcessor processor, IChunk chunk, IChunkGet get, IChunkSet chunkSet) { + if (processor instanceof Filter) { + chunkSet = ((IBatchProcessor) classToThreadIdToFilter.getUnchecked(processor.getClass()) + .computeIfAbsent(Thread.currentThread().getId(), k -> ((Filter) processor).fork())).processSet(chunk, get, chunkSet); + } else { + chunkSet = processor.processSet(chunk, get, chunkSet); + } + return chunkSet; + } + @Override public Future postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) { try { - for (int i = processors.length - 1 ; i >= 0; i--) { - IBatchProcessor processor = processors[i]; + for (IBatchProcessor processor : processors) { + // We do NOT want to edit blocks in post processing + if (processor.getScope() != ProcessorScope.READING_SET_BLOCKS) { + continue; + } set = processor.postProcessSet(chunk, get, set).get(); if (set == null) { return null; @@ -163,4 +199,13 @@ public class MultiBatchProcessor implements IBatchProcessor { public String toString() { return super.toString() + "{" + StringMan.join(processors, ",") + "}"; } + + @Override + public ProcessorScope getScope() { + int scope = 0; + for (IBatchProcessor processor : processors) { + scope = Math.max(scope, processor.getScope().intValue()); + } + return ProcessorScope.valueOf(0); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java index dbabe51f0..c5127feaa 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/NullProcessor.java @@ -36,4 +36,9 @@ public final class NullProcessor implements IBatchProcessor { private NullProcessor() { } + @Override + public ProcessorScope getScope() { + return ProcessorScope.ADDING_BLOCKS; + } + } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ProcessorScope.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ProcessorScope.java new file mode 100644 index 000000000..cc8aae35e --- /dev/null +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/ProcessorScope.java @@ -0,0 +1,40 @@ +package com.boydti.fawe.beta.implementation.processors; + +/** + * The scope of a processor. + * Order in which processors are executed: + * - ADDING_BLOCKS (processors that strictly ADD blocks to an edit ONLY) + * - CHANGING_BLOCKS (processors that strictly ADD or CHANGE blocks being set) + * - REMOVING_BLOCKS (processors that string ADD, CHANGE or REMOVE blocks being set) + * - CUSTOM (processors that do not specify a SCOPE) + * - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g. history processors) + */ +public enum ProcessorScope { + ADDING_BLOCKS(0), CHANGING_BLOCKS(1), REMOVING_BLOCKS(2), CUSTOM(3), READING_SET_BLOCKS(4); + + private final int value; + + ProcessorScope(int value) { + this.value = value; + } + + public int intValue() { + return this.value; + } + + public static ProcessorScope valueOf(int value) { + switch (value) { + case 0: + return ProcessorScope.ADDING_BLOCKS; + case 1: + return ProcessorScope.CHANGING_BLOCKS; + case 2: + return ProcessorScope.REMOVING_BLOCKS; + case 4: + return ProcessorScope.READING_SET_BLOCKS; + case 3: + default: + return ProcessorScope.CUSTOM; + } + } +} diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java index 65b43a342..25c64cbcf 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/queue/SingleThreadQueueExtent.java @@ -14,6 +14,7 @@ import com.boydti.fawe.beta.implementation.filter.block.CharFilterBlock; import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor; import com.boydti.fawe.beta.implementation.processors.ExtentBatchProcessorHolder; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.boydti.fawe.config.Settings; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.MemUtil; @@ -352,4 +353,9 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen public ChunkFilterBlock initFilterBlock() { return new CharFilterBlock(this); } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.ADDING_BLOCKS; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java index 07b99a456..96cf60469 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java @@ -623,6 +623,15 @@ public class MCAChunk implements IChunk { return createCopy; } + @Override + public void setLightingToGet(char[][] lighting) {} + + @Override + public void setSkyLightingToGet(char[][] lighting) {} + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) {} + @Override public Future call(IChunkSet set, Runnable finalize) { return null; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java index 7ee81bb94..9de6569be 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java @@ -6,6 +6,7 @@ import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.boydti.fawe.object.HistoryExtent; import com.boydti.fawe.util.EditSessionBuilder; import com.boydti.fawe.util.MainUtil; @@ -212,6 +213,11 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { return (Future) addWriteTask(() -> processSet(chunk, get, set)); } + @Override + public ProcessorScope getScope() { + return ProcessorScope.READING_SET_BLOCKS; + } + public abstract void addTileCreate(CompoundTag tag); public abstract void addTileRemove(CompoundTag tag); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java index b53d7ff0f..71e06f5b4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java @@ -2,6 +2,7 @@ package com.boydti.fawe.object.extent; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IBatchProcessor; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.util.ExtentTraverser; import com.boydti.fawe.util.WEManager; @@ -147,4 +148,9 @@ public abstract class FaweRegionExtent extends ResettableExtent implements IBatc } return super.createEntity(location, entity); } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.READING_SET_BLOCKS; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java index 24e5ed955..84518c95f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java @@ -5,6 +5,7 @@ import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.exception.FaweException; import com.sk89q.jnbt.CompoundTag; @@ -355,4 +356,9 @@ public class NullExtent extends FaweRegionExtent implements IBatchProcessor { public Extent construct(Extent child) { throw reason; } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.ADDING_BLOCKS; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/regions/FaweMask.java b/worldedit-core/src/main/java/com/boydti/fawe/regions/FaweMask.java index e8f7ee586..f08c3f208 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/regions/FaweMask.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/regions/FaweMask.java @@ -1,5 +1,6 @@ package com.boydti.fawe.regions; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.regions.IDelegateRegion; import com.sk89q.worldedit.regions.Region; @@ -24,4 +25,9 @@ public class FaweMask implements IDelegateRegion { public Region clone() { throw new UnsupportedOperationException("Clone not supported"); } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.REMOVING_BLOCKS; + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java index e2bf8705c..1ba46a53a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/EditSessionBuilder.java @@ -4,6 +4,10 @@ import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.IQueueChunk; import com.boydti.fawe.beta.IQueueExtent; +import com.boydti.fawe.beta.implementation.lighting.NMSRelighter; +import com.boydti.fawe.beta.implementation.lighting.NullRelighter; +import com.boydti.fawe.beta.implementation.lighting.RelightProcessor; +import com.boydti.fawe.beta.implementation.lighting.Relighter; import com.boydti.fawe.beta.implementation.processors.LimitExtent; import com.boydti.fawe.beta.implementation.queue.ParallelQueueExtent; import com.boydti.fawe.config.Settings; @@ -67,6 +71,7 @@ public class EditSessionBuilder { private EditSessionEvent event; private String command; private RelightMode relightMode; + private Relighter relighter; private Boolean wnaMode; /** @@ -396,6 +401,14 @@ public class EditSessionBuilder { allowedRegions = new Region[]{RegionWrapper.GLOBAL()}; // this.extent = new HeightBoundExtent(this.extent, this.limit, 0, world.getMaxY()); } + // There's no need to do lighting (and it'll also just be a pain to implement) if we're not placing chunks + if (placeChunks && ((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.IMP.LIGHTING.MODE > 0))) { + relighter = new NMSRelighter(queue, Settings.IMP.LIGHTING.DO_HEIGHTMAPS, + relightMode != null ? relightMode : RelightMode.valueOf(Settings.IMP.LIGHTING.MODE)); + extent.addProcessor(new RelightProcessor(relighter)); + } else { + relighter = NullRelighter.INSTANCE; + } if (limit != null && !limit.isUnlimited() && regionExtent != null) { this.extent = new LimitExtent(regionExtent, limit); } else if (limit != null && !limit.isUnlimited()) { @@ -454,6 +467,10 @@ public class EditSessionBuilder { return blockBag; } + public Relighter getRelighter() { + return relighter; + } + public boolean isWNAMode() { return wnaMode; } @@ -462,5 +479,4 @@ public class EditSessionBuilder { public Region[] getAllowedRegions() { return allowedRegions; } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 60e2c92c2..36870aeef 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -20,6 +20,8 @@ package com.sk89q.worldedit; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.implementation.lighting.NullRelighter; +import com.boydti.fawe.beta.implementation.lighting.Relighter; import com.boydti.fawe.config.Caption; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweLimit; @@ -224,12 +226,12 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { private final int maxY; private final List watchdogExtents = new ArrayList<>(2); + private final Relighter relighter; private final boolean wnaMode; @Nullable private final Region[] allowedRegions; - @Deprecated public EditSession(@NotNull EventBus bus, World world, @Nullable Player player, @Nullable FaweLimit limit, @Nullable AbstractChangeSet changeSet, @@ -264,6 +266,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { this.maxY = world.getMaxY(); this.blockBag = builder.getBlockBag(); this.history = changeSet != null; + this.relighter = builder.getRelighter(); this.wnaMode = builder.isWNAMode(); this.allowedRegions = builder.getAllowedRegions() != null ? builder.getAllowedRegions().clone() : null; } @@ -1093,6 +1096,25 @@ public class EditSession extends PassthroughExtent implements AutoCloseable { } // Reset limit limit.set(originalLimit); + try { + if (relighter != null && !(relighter instanceof NullRelighter)) { + // Only relight once! + if (!relighter.getLock().tryLock()) { + relighter.getLock().lock(); + relighter.getLock().unlock(); + } else { + if (Settings.IMP.LIGHTING.REMOVE_FIRST) { + relighter.removeAndRelight(true); + } else { + relighter.fixSkyLighting(); + relighter.fixBlockLighting(); + } + } + } + } catch (Throwable e) { + player.printError(TranslatableComponent.of("fawe.error.lighting")); + e.printStackTrace(); + } // Enqueue it if (getChangeSet() != null) { if (Settings.IMP.HISTORY.COMBINE_STAGES) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java index f16a67d5b..62587d94a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/Extent.java @@ -23,6 +23,7 @@ import com.boydti.fawe.FaweCache; import com.boydti.fawe.beta.Filter; import com.boydti.fawe.beta.IBatchProcessor; import com.boydti.fawe.beta.implementation.filter.block.ExtentFilterBlock; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.changeset.AbstractChangeSet; import com.boydti.fawe.object.clipboard.WorldCopyClipboard; @@ -707,6 +708,9 @@ public interface Extent extends InputExtent, OutputExtent { } default Extent addPostProcessor(IBatchProcessor processor) { + if (processor.getScope() == ProcessorScope.READING_SET_BLOCKS) { + throw new IllegalArgumentException("You cannot alter blocks in a PostProcessor"); + } return processor.construct(this); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java index 1d0078eef..09f39546e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/MaskingExtent.java @@ -28,6 +28,7 @@ import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.filter.block.CharFilterBlock; import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; import com.boydti.fawe.beta.implementation.filter.block.FilterBlock; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.google.common.cache.LoadingCache; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.function.mask.Mask; @@ -128,4 +129,9 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce public Filter fork() { return new MaskingExtent(getExtent(), this.mask.copy(), this.threadIdToFilter); } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.REMOVING_BLOCKS; + } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index 730707c87..4c5ab15d1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -25,6 +25,7 @@ import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.collection.BlockVectorSet; import com.sk89q.worldedit.math.BlockVector2; @@ -774,5 +775,4 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { return null; } - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index 6f75ea8f6..4f20cc0c3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -25,6 +25,7 @@ import com.boydti.fawe.beta.IChunk; import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.IChunkSet; import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock; +import com.boydti.fawe.beta.implementation.processors.ProcessorScope; import com.boydti.fawe.object.FaweLimit; import com.boydti.fawe.object.extent.SingleRegionExtent; import com.sk89q.worldedit.extent.Extent; @@ -365,4 +366,9 @@ public interface Region extends Iterable, Cloneable, IBatchProcess } return new SingleRegionExtent(child, FaweLimit.MAX, this); } + + @Override + default ProcessorScope getScope() { + return ProcessorScope.REMOVING_BLOCKS; + } } diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 108294c80..9fc51037c 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -104,6 +104,7 @@ "fawe.error.worldedit.some.fails.blockbag": "Missing blocks: {0}", "fawe.error.mask.angle": "Cannot combine degree with block-step", "fawe.error.invalid-flag": "The flag {0} is not applicable here", + "fawe.error.lighting": "Error when attempting lighting. You may need to reload the chunks to see the edit.", "fawe.cancel.worldedit.cancel.count": "Cancelled {0} edits.", "fawe.cancel.worldedit.cancel.reason.confirm": "Use //confirm to execute {2}",