diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java
index 7998d58b5..b2ed81ce9 100644
--- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java
+++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java
@@ -30,6 +30,7 @@ import net.minecraft.server.v1_14_R1.DataPaletteBlock;
import net.minecraft.server.v1_14_R1.DataPaletteLinear;
import net.minecraft.server.v1_14_R1.GameProfileSerializer;
import net.minecraft.server.v1_14_R1.IBlockData;
+import net.minecraft.server.v1_14_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_14_R1.PlayerChunk;
import net.minecraft.server.v1_14_R1.PlayerChunkMap;
import net.minecraft.server.v1_14_R1.World;
@@ -167,7 +168,7 @@ public final class BukkitAdapter_1_14 extends NMSAdapter {
}
}
- public static void sendChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, int X, int Z, int mask) {
+ public static void sendChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) {
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
if (playerChunk == null) {
return;
@@ -187,6 +188,15 @@ public final class BukkitAdapter_1_14 extends NMSAdapter {
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
+
+ if (lighting) {
+ ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z);
+ PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
+ playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
+ p.playerConnection.sendPacket(packet);
+ });
+ }
+
} catch (IllegalAccessException e) {
e.printStackTrace();
}
diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java
index db000c51d..848d127c5 100644
--- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java
+++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java
@@ -54,10 +54,13 @@ import net.minecraft.server.v1_14_R1.DataPaletteHash;
import net.minecraft.server.v1_14_R1.DataPaletteLinear;
import net.minecraft.server.v1_14_R1.Entity;
import net.minecraft.server.v1_14_R1.EntityTypes;
+import net.minecraft.server.v1_14_R1.EnumSkyBlock;
import net.minecraft.server.v1_14_R1.IBlockData;
import net.minecraft.server.v1_14_R1.LightEngineThreaded;
import net.minecraft.server.v1_14_R1.NBTTagCompound;
import net.minecraft.server.v1_14_R1.NBTTagInt;
+import net.minecraft.server.v1_14_R1.NibbleArray;
+import net.minecraft.server.v1_14_R1.SectionPosition;
import net.minecraft.server.v1_14_R1.TileEntity;
import net.minecraft.server.v1_14_R1.WorldServer;
import org.bukkit.World;
@@ -72,6 +75,8 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
public Chunk nmsChunk;
public WorldServer world;
public int X, Z;
+ public NibbleArray[] blockLight = new NibbleArray[16];
+ public NibbleArray[] skyLight = new NibbleArray[16];
public BukkitGetBlocks_1_14(World world, int X, int Z) {
this(((CraftWorld) world).getHandle(), X, Z);
@@ -119,6 +124,26 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
}
+ @Override
+ public int getSkyLight(int x, int y, int z) {
+ int layer = y >> 4;
+ if (skyLight[layer] == null) {
+ skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer));
+ }
+ long l = BlockPosition.a(x, y, z);
+ return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
+ }
+
+ @Override
+ public int getEmmittedLight(int x, int y, int z) {
+ int layer = y >> 4;
+ if (blockLight[layer] == null) {
+ blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer));
+ }
+ long l = BlockPosition.a(x, y, z);
+ return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
+ }
+
@Override
public CompoundTag getEntity(UUID uuid) {
Entity entity = world.getEntity(uuid);
@@ -322,6 +347,29 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
}
}
+ 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();
+ }
+ }
+
Runnable[] syncTasks = null;
int bx = X << 4;
@@ -427,24 +475,19 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
};
}
- {//Lighting
- // TODO optimize, cause this is really slow
- LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e();
- engine.a(nmsChunk, false);
- }
-
Runnable callback;
- if (bitMask == 0 && biomes == null) {
+ if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
- int finalMask = bitMask;
+ int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
+ boolean finalLightUpdate = lightUpdate;
callback = () -> {
// Set Modified
nmsChunk.d(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
- BukkitAdapter_1_14.sendChunk(nmsWorld, X, Z, finalMask);
+ BukkitAdapter_1_14.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate);
if (finalizer != null) finalizer.run();
};
}
@@ -617,6 +660,25 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
return tmp;
}
+ private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
+ for (int Y = 0; Y < 16; Y++) {
+ if (light[Y] == null) {
+ continue;
+ }
+ NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y));
+ if (nibble == null) {
+ continue;
+ }
+ synchronized (nibble) {
+ for (int i = 0; i < 4096; i++) {
+ if (light[Y][i] < 16) {
+ nibble.a(i, light[Y][i]);
+ }
+ }
+ }
+ }
+ }
+
@Override
public boolean hasSection(int layer) {
return getSections()[layer] != null;
diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java
index d2c2c4c29..be791aa66 100644
--- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java
+++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitAdapter_1_15.java
@@ -24,6 +24,7 @@ import net.minecraft.server.v1_15_R1.DataPaletteBlock;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.GameProfileSerializer;
import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_15_R1.PlayerChunk;
import net.minecraft.server.v1_15_R1.PlayerChunkMap;
import net.minecraft.server.v1_15_R1.World;
@@ -85,6 +86,8 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
fieldDirtyBits = PlayerChunk.class.getDeclaredField("r");
fieldDirtyBits.setAccessible(true);
+ fieldTickingBlockCount.setAccessible(true);
+
Method declaredGetVisibleChunk = PlayerChunkMap.class.getDeclaredMethod("getVisibleChunk", long.class);
declaredGetVisibleChunk.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
@@ -165,7 +168,7 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
}
}
- public static void sendChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, int X, int Z, int mask) {
+ public static void sendChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) {
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
if (playerChunk == null) {
return;
@@ -185,6 +188,15 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
+
+ if (lighting) {
+ ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z);
+ PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
+ playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
+ p.playerConnection.sendPacket(packet);
+ });
+ }
+
} catch (IllegalAccessException e) {
e.printStackTrace();
}
diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java
index 21c5f48f7..c9fbca585 100644
--- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java
+++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java
@@ -55,10 +55,13 @@ import net.minecraft.server.v1_15_R1.DataPaletteHash;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.Entity;
import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EnumSkyBlock;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.LightEngineThreaded;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.NBTTagInt;
+import net.minecraft.server.v1_15_R1.NibbleArray;
+import net.minecraft.server.v1_15_R1.SectionPosition;
import net.minecraft.server.v1_15_R1.TileEntity;
import net.minecraft.server.v1_15_R1.WorldServer;
import org.bukkit.World;
@@ -75,6 +78,8 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
public Chunk nmsChunk;
public WorldServer world;
public int X, Z;
+ public NibbleArray[] blockLight = new NibbleArray[16];
+ public NibbleArray[] skyLight = new NibbleArray[16];
public BukkitGetBlocks_1_15(World world, int X, int Z) {
this(((CraftWorld) world).getHandle(), X, Z);
@@ -127,6 +132,26 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
}
+ @Override
+ public int getSkyLight(int x, int y, int z) {
+ int layer = y >> 4;
+ if (skyLight[layer] == null) {
+ skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer));
+ }
+ long l = BlockPosition.a(x, y, z);
+ return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
+ }
+
+ @Override
+ public int getEmmittedLight(int x, int y, int z) {
+ int layer = y >> 4;
+ if (blockLight[layer] == null) {
+ blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer));
+ }
+ long l = BlockPosition.a(x, y, z);
+ return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
+ }
+
@Override
public CompoundTag getEntity(UUID uuid) {
Entity entity = world.getEntity(uuid);
@@ -335,6 +360,29 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
}
}
+ 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();
+ }
+ }
+
Runnable[] syncTasks = null;
int bx = X << 4;
@@ -440,25 +488,19 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
};
}
- //Lighting
- // TODO optimize, cause this is really slow
- LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e();
- //lightChunk()
- engine.a(nmsChunk, false);
-
Runnable callback;
- if (bitMask == 0 && biomes == null) {
+ if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
- int finalMask = bitMask;
+ int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
+ boolean finalLightUpdate = lightUpdate;
callback = () -> {
// Set Modified
- //setLastSaveHadEntities
nmsChunk.d(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
- BukkitAdapter_1_15.sendChunk(nmsWorld, X, Z, finalMask);
+ BukkitAdapter_1_15.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate);
if (finalizer != null) finalizer.run();
};
}
@@ -637,6 +679,25 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
return tmp;
}
+ private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
+ for (int Y = 0; Y < 16; Y++) {
+ if (light[Y] == null) {
+ continue;
+ }
+ NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y));
+ if (nibble == null) {
+ continue;
+ }
+ synchronized (nibble) {
+ for (int i = 0; i < 4096; i++) {
+ if (light[Y][i] < 16) {
+ nibble.a(i, light[Y][i]);
+ }
+ }
+ }
+ }
+ }
+
@Override
public boolean hasSection(int layer) {
return getSections()[layer] != null;
diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java
index 466cb233a..27cceceee 100644
--- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java
+++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitAdapter_1_15_2.java
@@ -13,12 +13,6 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.locks.ReentrantLock;
import net.jpountz.util.UnsafeUtils;
import net.minecraft.server.v1_15_R1.Block;
import net.minecraft.server.v1_15_R1.Chunk;
@@ -30,6 +24,11 @@ import net.minecraft.server.v1_15_R1.DataPaletteBlock;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.GameProfileSerializer;
import net.minecraft.server.v1_15_R1.IBlockData;
+import net.minecraft.server.v1_15_R1.LightEngine;
+import net.minecraft.server.v1_15_R1.LightEngineLayer;
+import net.minecraft.server.v1_15_R1.LightEngineStorage;
+import net.minecraft.server.v1_15_R1.NibbleArray;
+import net.minecraft.server.v1_15_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_15_R1.PlayerChunk;
import net.minecraft.server.v1_15_R1.PlayerChunkMap;
import net.minecraft.server.v1_15_R1.World;
@@ -38,9 +37,15 @@ import org.bukkit.craftbukkit.v1_15_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
import sun.misc.Unsafe;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
public final class BukkitAdapter_1_15_2 extends NMSAdapter {
@@ -60,6 +65,8 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
private final static MethodHandle methodGetVisibleChunk;
+ public final static MethodHandle methodSetLightNibbleArray;
+
private static final int CHUNKSECTION_BASE;
private static final int CHUNKSECTION_SHIFT;
@@ -90,6 +97,10 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
declaredGetVisibleChunk.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
+ Method declaredSetLightNibbleArray = LightEngineStorage.class.getDeclaredMethod("a", long.class, NibbleArray.class);
+ declaredSetLightNibbleArray.setAccessible(true);
+ methodSetLightNibbleArray = MethodHandles.lookup().unreflect(declaredSetLightNibbleArray);
+
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
ReflectionUtils.setAccessibleNonFinal(tmp);
fieldLock = tmp;
@@ -167,7 +178,7 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
}
}
- public static void sendChunk(WorldServer nmsWorld, int X, int Z, int mask) {
+ public static void sendChunk(WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) {
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
if (playerChunk == null) {
return;
@@ -187,6 +198,15 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
+
+ if (lighting) {
+ ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z);
+ PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
+ playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
+ p.playerConnection.sendPacket(packet);
+ });
+ }
+
} catch (IllegalAccessException e) {
e.printStackTrace();
}
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 efe33059b..3292cc556 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
@@ -38,10 +38,12 @@ import net.minecraft.server.v1_15_R1.DataPaletteHash;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.Entity;
import net.minecraft.server.v1_15_R1.EntityTypes;
+import net.minecraft.server.v1_15_R1.EnumSkyBlock;
import net.minecraft.server.v1_15_R1.IBlockData;
-import net.minecraft.server.v1_15_R1.LightEngineThreaded;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.NBTTagInt;
+import net.minecraft.server.v1_15_R1.NibbleArray;
+import net.minecraft.server.v1_15_R1.SectionPosition;
import net.minecraft.server.v1_15_R1.TileEntity;
import net.minecraft.server.v1_15_R1.WorldServer;
import org.bukkit.World;
@@ -80,6 +82,8 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
public Chunk nmsChunk;
public WorldServer world;
public int X, Z;
+ public NibbleArray[] blockLight = new NibbleArray[16];
+ public NibbleArray[] skyLight = new NibbleArray[16];
public BukkitGetBlocks_1_15_2(World world, int X, int Z) {
this(((CraftWorld) world).getHandle(), X, Z);
@@ -132,6 +136,26 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
}
+ @Override
+ public int getSkyLight(int x, int y, int z) {
+ int layer = y >> 4;
+ if (skyLight[layer] == null) {
+ skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer));
+ }
+ long l = BlockPosition.a(x, y, z);
+ return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
+ }
+
+ @Override
+ public int getEmmittedLight(int x, int y, int z) {
+ int layer = y >> 4;
+ if (blockLight[layer] == null) {
+ blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer));
+ }
+ long l = BlockPosition.a(x, y, z);
+ return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
+ }
+
@Override
public CompoundTag getEntity(UUID uuid) {
Entity entity = world.getEntity(uuid);
@@ -347,6 +371,29 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
}
}
+ 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();
+ }
+ }
+
Runnable[] syncTasks = null;
int bx = X << 4;
@@ -452,23 +499,19 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
};
}
- //Lighting
- // TODO optimize, cause this is really slow
- LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e();
- engine.a(nmsChunk, false);
-
Runnable callback;
- if (bitMask == 0 && biomes == null) {
+ if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
- int finalMask = bitMask;
+ int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
+ boolean finalLightUpdate = lightUpdate;
callback = () -> {
// Set Modified
nmsChunk.d(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
- BukkitAdapter_1_15_2.sendChunk(nmsWorld, X, Z, finalMask);
+ BukkitAdapter_1_15_2.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate);
if (finalizer != null) finalizer.run();
};
}
@@ -641,6 +684,25 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
return tmp;
}
+ private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
+ for (int Y = 0; Y < 16; Y++) {
+ if (light[Y] == null) {
+ continue;
+ }
+ NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y));
+ if (nibble == null) {
+ continue;
+ }
+ synchronized (nibble) {
+ for (int i = 0; i < 4096; i++) {
+ if (light[Y][i] < 16) {
+ nibble.a(i, light[Y][i]);
+ }
+ }
+ }
+ }
+ }
+
@Override
public boolean hasSection(int layer) {
return getSections()[layer] != null;
@@ -648,6 +710,8 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
@Override
public boolean trim(boolean aggressive) {
+ skyLight = new NibbleArray[16];
+ blockLight = new NibbleArray[16];
if (aggressive) {
sections = null;
nmsChunk = null;
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 3548112d7..245db7791 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java
+++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java
@@ -2,8 +2,11 @@ package com.boydti.fawe;
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.queue.ParallelQueueExtent;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.RegionWrapper;
+import com.boydti.fawe.object.RelightMode;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.SimpleChangeSetSummary;
import com.boydti.fawe.object.exception.FaweException;
@@ -28,10 +31,13 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
import com.sk89q.worldedit.internal.registry.AbstractFactory;
import com.sk89q.worldedit.internal.registry.InputParser;
+import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.World;
+
+import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
@@ -43,7 +49,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
-import javax.annotation.Nullable;
/**
* The FaweAPI class offers a few useful functions.
@@ -387,12 +392,52 @@ public class FaweAPI {
* - Relights in parallel (if enabled) for best performance
* - Also resends chunks
*
- * @param world
- * @param selection (assumes cuboid)
- * @return
+ * @param world World to relight in
+ * @param selection Region to relight
+ * @param queue Queue to relight in/from
+ * @param mode The mode to relight with
+ * @return Chunks changed
*/
- public static int fixLighting(World world, Region selection, @Nullable IQueueExtent queue) {
- throw new UnsupportedOperationException();
+ public static int fixLighting(World world, Region selection, @Nullable IQueueExtent queue, final RelightMode mode) {
+ final BlockVector3 bot = selection.getMinimumPoint();
+ final BlockVector3 top = selection.getMaximumPoint();
+
+ final int minX = bot.getBlockX() >> 4;
+ final int minZ = bot.getBlockZ() >> 4;
+
+ final int maxX = top.getBlockX() >> 4;
+ final int maxZ = top.getBlockZ() >> 4;
+
+ int count = 0;
+
+ if (queue == null) {
+ World unwrapped = WorldWrapper.unwrap(world);
+ if (unwrapped instanceof IQueueExtent) {
+ queue = (IQueueExtent) unwrapped;
+ } else if (Settings.IMP.QUEUE.PARALLEL_THREADS > 1) {
+ ParallelQueueExtent parallel =
+ new ParallelQueueExtent(Fawe.get().getQueueHandler(), world, true);
+ queue = parallel.getExtent();
+ } else {
+ queue = Fawe.get().getQueueHandler().getQueue(world);
+ }
+ }
+
+ NMSRelighter relighter = new NMSRelighter(queue);
+ for (int x = minX; x <= maxX; x++) {
+ for (int z = minZ; z <= maxZ; z++) {
+ relighter.addChunk(x, z, null, 65535);
+ count++;
+ }
+ }
+ if (mode != RelightMode.NONE) {
+ relighter.fixSkyLighting();
+ relighter.fixBlockLighting();
+ } else {
+ relighter.removeLighting();
+ }
+ relighter.sendChunks();
+ return count;
}
/**
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 9061dfdf1..c0eb29a35 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
@@ -26,6 +26,12 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput {
@Override
BlockState getBlock(int x, int y, int z);
+ @Override
+ int getSkyLight(int x, int y, int z);
+
+ @Override
+ int getEmmittedLight(int x, int y, int z);
+
default void optimize() {
}
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 168120f46..843922f45 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
@@ -28,6 +28,20 @@ public interface IChunkSet extends IBlocks, OutputExtent {
@Override
boolean setTile(int x, int y, int z, CompoundTag tile);
+ @Override
+ void setBlockLight(int x, int y, int z, int value);
+
+ @Override
+ void setSkyLight(int x, int y, int z, int value);
+
+ void setLightLayer(int layer, char[] toSet);
+
+ void setSkyLightLayer(int layer, char[] toSet);
+
+ void removeSectionLighting(int layer, boolean sky);
+
+ void setFullBright(int layer);
+
void setEntity(CompoundTag tag);
void removeEntity(UUID uuid);
@@ -40,12 +54,27 @@ public interface IChunkSet extends IBlocks, OutputExtent {
return getBiomes() != null;
}
+ char[][] getLight();
+
+ char[][] getSkyLight();
+
+ default boolean hasLight() {
+ return getLight() != null;
+ }
+
+ // Default to avoid tricky child classes. We only need it in a few cases anyway.
+ default void setFastMode(boolean fastMode){}
+
default boolean isFastMode() {
return false;
}
- //default to avoid tricky child classes. We only need it in a few cases anyway.
- default void setFastMode(boolean fastMode){}
+ // Allow setting for bitmask for flushing lighting. Default to avoid tricky child classes.
+ default void setBitMask(int bitMask){}
+
+ default int getBitMask(){
+ return -1;
+ }
@Override
IChunkSet reset();
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkExtent.java
index 146767571..5ebffe8cc 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkExtent.java
+++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkExtent.java
@@ -55,4 +55,40 @@ public interface IChunkExtent extends Extent {
final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
return chunk.getBiomeType(x & 15, y, z & 15);
}
+
+ @Override
+ default void setSkyLight(int x, int y, int z, int value) {
+ final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
+ chunk.setSkyLight(x & 15, y, z & 15, value);
+ }
+
+ @Override
+ default void setBlockLight(int x, int y, int z, int value) {
+ final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
+ chunk.setSkyLight(x & 15, y, z & 15, value);
+ }
+
+ @Override
+ default int getSkyLight(int x, int y, int z) {
+ final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
+ return chunk.getSkyLight(x & 15, y, z & 15);
+ }
+
+ @Override
+ default int getEmmittedLight(int x, int y, int z) {
+ final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
+ return chunk.getEmmittedLight(x & 15, y, z & 15);
+ }
+
+ @Override
+ default int getBrightness(int x, int y, int z) {
+ final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
+ return chunk.getBrightness(x & 15, y, z & 15);
+ }
+
+ @Override
+ default int getOpacity(int x, int y, int z) {
+ final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
+ return chunk.getOpacity(x & 15, y, z & 15);
+ }
}
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java
index 4d7a6a21b..34af72698 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java
+++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java
@@ -66,6 +66,18 @@ public class BitSetBlocks implements IChunkSet {
return false;
}
+ @Override public void setBlockLight(int x, int y, int z, int value) {}
+
+ @Override public void setSkyLight(int x, int y, int z, int value) {}
+
+ @Override public void setLightLayer(int layer, char[] toSet) {}
+
+ @Override public void setSkyLightLayer(int layer, char[] toSet) {}
+
+ @Override public void removeSectionLighting(int layer, boolean sky) {}
+
+ @Override public void setFullBright(int layer) {}
+
@Override
public void setEntity(CompoundTag tag) {
}
@@ -118,6 +130,14 @@ public class BitSetBlocks implements IChunkSet {
return null;
}
+ @Override public char[][] getLight() {
+ return new char[0][];
+ }
+
+ @Override public char[][] getSkyLight() {
+ return new char[0][];
+ }
+
@Override
public BiomeType getBiomeType(int x, int y, int z) {
return null;
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java
index b7eda2724..1dda73c3a 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java
+++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java
@@ -5,24 +5,21 @@ import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.queue.Pool;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.collection.BlockVector3ChunkMap;
-import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
-import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
-import com.sk89q.worldedit.world.block.BlockTypes;
-import com.sk89q.worldedit.world.block.BlockTypesCache;
+import org.jetbrains.annotations.Range;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.IntStream;
-import org.jetbrains.annotations.Range;
public class CharSetBlocks extends CharBlocks implements IChunkSet {
private static final Pool POOL = FaweCache.IMP.registerPool(CharSetBlocks.class, CharSetBlocks::new, Settings.IMP.QUEUE.POOL);
@@ -31,10 +28,13 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
}
public BiomeType[] biomes;
+ public char[][] light;
+ public char[][] skyLight;
public BlockVector3ChunkMap tiles;
public HashSet entities;
public HashSet entityRemoves;
private boolean fastMode = false;
+ private int bitMask = -1;
private CharSetBlocks() {}
@@ -111,6 +111,80 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
return true;
}
+ @Override public void setBlockLight(int x, int y, int z, int value) {
+ if (light == null) {
+ light = new char[16][];
+ }
+ final int layer = y >> 4;
+ if (light[layer] == null) {
+ char[] c = new char[4096];
+ Arrays.fill(c, (char) 16);
+ light[layer] = c;
+ }
+ final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15);
+ light[y >> 4][index] = (char) value;
+ }
+
+ @Override public void setSkyLight(int x, int y, int z, int value) {
+ if (skyLight == null) {
+ skyLight = new char[16][];
+ }
+ final int layer = y >> 4;
+ if (skyLight[layer] == null) {
+ char[] c = new char[4096];
+ Arrays.fill(c, (char) 16);
+ skyLight[layer] = c;
+ }
+ final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15);
+ skyLight[y >> 4][index] = (char) value;
+ }
+
+ @Override public void setLightLayer(int layer, char[] toSet) {
+ if (light == null) {
+ light = new char[16][];
+ }
+ light[layer] = toSet;
+ }
+
+ @Override public void setSkyLightLayer(int layer, char[] toSet) {
+ if (skyLight == null) {
+ skyLight = new char[16][];
+ }
+ skyLight[layer] = toSet;
+ }
+
+ @Override public char[][] getLight() {
+ return light;
+ }
+
+ @Override public char[][] getSkyLight() {
+ return skyLight;
+ }
+
+ @Override public void removeSectionLighting(int layer, boolean sky) {
+ if (light == null) {
+ light = new char[16][];
+ }
+ if (light[layer] == null) {
+ light[layer] = new char[4096];
+ }
+ Arrays.fill(light[layer], (char) 0);
+ if (sky) {
+ if (skyLight == null) {
+ skyLight = new char[16][];
+ }
+ if (skyLight[layer] == null) {
+ skyLight[layer] = new char[4096];
+ }
+ Arrays.fill(skyLight[layer], (char) 0);
+ }
+ }
+
+ @Override public void setFullBright(int layer) {
+ Arrays.fill(light[layer], (char) 15);
+ Arrays.fill(skyLight[layer], (char) 15);
+ }
+
@Override
public boolean setBiome(BlockVector2 position, BiomeType biome) {
return setBiome(position.getX(),0, position.getZ(), biome);
@@ -142,9 +216,19 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
return fastMode;
}
+ @Override
+ public void setBitMask(int bitMask) {
+ this.bitMask = bitMask;
+ }
+
+ @Override
+ public int getBitMask() {
+ return bitMask;
+ }
+
@Override
public boolean isEmpty() {
- if (biomes != null) {
+ if (biomes != null || light != null || skyLight != null) {
return false;
}
return IntStream.range(0, 16).noneMatch(this::hasSection);
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 c6b596f43..9da99934d 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
@@ -41,6 +41,14 @@ public class FallbackChunkGet implements IChunkGet {
return extent.getBlock(bx + x, y, bz + z);
}
+ @Override public int getSkyLight(int x, int y, int z) {
+ return extent.getSkyLight(bx + x, y, bz + z);
+ }
+
+ @Override public int getEmmittedLight(int x, int y, int z) {
+ return extent.getEmmittedLight(bx + x, y, bz + z);
+ }
+
@Override
public CompoundTag getTile(int x, int y, int z) {
return extent.getFullBlock(bx + x, y, bz + z).getNbtData();
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.kt b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.kt
index a7a5e43de..cd43cb7c1 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.kt
+++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.kt
@@ -12,7 +12,6 @@ import com.sk89q.worldedit.world.block.BaseBlock
import com.sk89q.worldedit.world.block.BlockState
import com.sk89q.worldedit.world.block.BlockTypes
-import java.util.Collections
import java.util.UUID
import java.util.concurrent.Future
@@ -66,6 +65,14 @@ object NullChunkGet : IChunkGet {
return false
}
+ override fun getEmmittedLight(x: Int, y: Int, z: Int): Int {
+ return 15
+ }
+
+ override fun getSkyLight(x: Int, y: Int, z: Int): Int {
+ return 15
+ }
+
override fun reset(): IBlocks? {
return null
}
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 7449cbec0..4be438efc 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
@@ -1,13 +1,13 @@
package com.boydti.fawe.beta.implementation.chunk;
import com.boydti.fawe.FaweCache;
-import com.boydti.fawe.beta.IQueueChunk;
-import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
+import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
+import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.queue.Pool;
import com.boydti.fawe.config.Settings;
import com.sk89q.jnbt.CompoundTag;
@@ -17,17 +17,18 @@ import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
+import org.jetbrains.annotations.Range;
+import javax.annotation.Nullable;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
-import javax.annotation.Nullable;
-import org.jetbrains.annotations.Range;
/**
* An abstract {@link IChunk} class that implements basic get/set blocks
*/
+@SuppressWarnings("rawtypes")
public class ChunkHolder> implements IQueueChunk {
private static final Pool POOL = FaweCache.IMP.registerPool(ChunkHolder.class, ChunkHolder::new, Settings.IMP.QUEUE.POOL);
@@ -43,6 +44,8 @@ public class ChunkHolder> implements IQueueChunk {
private int chunkX;
private int chunkZ;
private boolean fastmode;
+ private int bitMask = -1; // Allow forceful setting of bitmask (for lighting)
+ private boolean isInit = false; // Lighting handles queue differently. It relies on the chunk cache and not doing init.
private ChunkHolder() {
this.delegate = NULL;
@@ -91,6 +94,14 @@ public class ChunkHolder> implements IQueueChunk {
return delegate.set(this).getBiomes(); // TODO return get?
}
+ @Override public char[][] getLight() {
+ return delegate.set(this).getLight();
+ }
+
+ @Override public char[][] getSkyLight() {
+ return delegate.set(this).getSkyLight();
+ }
+
@Override
public void setBlocks(int layer, char[] data) {
delegate.set(this).setBlocks(layer, data);
@@ -111,6 +122,18 @@ public class ChunkHolder> implements IQueueChunk {
this.fastmode = fastmode;
}
+ public void setBitMask(int bitMask) {
+ this.bitMask = bitMask;
+ }
+
+ public int getBitMask() {
+ return bitMask;
+ }
+
+ public boolean isInit() {
+ return isInit;
+ }
+
@Override
public CompoundTag getEntity(UUID uuid) {
return delegate.get(this).getEntity(uuid);
@@ -139,6 +162,36 @@ public class ChunkHolder> implements IQueueChunk {
return chunk.chunkSet.setBlock(x, y, z, block);
}
+ @Override
+ public void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value) {
+ chunk.chunkSet.setSkyLight(x, y, z, value);
+ }
+
+ @Override
+ public void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value) {
+ chunk.chunkSet.setBlockLight(x, y, z, value);
+ }
+
+ @Override
+ public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
+ chunk.chunkSet.removeSectionLighting(layer, sky);
+ }
+
+ @Override
+ public void setFullBright(ChunkHolder chunk, int layer){
+ chunk.chunkSet.setFullBright(layer);
+ }
+
+ @Override
+ public void setLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
+ chunk.chunkSet.setLightLayer(layer, toSet);
+ }
+
+ @Override
+ public void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
+ chunk.chunkSet.setSkyLightLayer(layer, toSet);
+ }
+
@Override
public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getBiomeType(x, y, z);
@@ -154,6 +207,44 @@ public class ChunkHolder> implements IQueueChunk {
int z) {
return chunk.chunkExisting.getFullBlock(x, y, z);
}
+
+ @Override
+ public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
+ if (chunk.chunkSet.getSkyLight() != null) {
+ int layer = y >> 4;
+ if (chunk.chunkSet.getSkyLight()[layer] != null) {
+ int setLightValue = chunk.chunkSet.getSkyLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
+ if (setLightValue < 16) {
+ return setLightValue;
+ }
+ }
+ }
+ return chunk.chunkExisting.getSkyLight(x, y, z);
+ }
+
+ @Override
+ public int getEmmittedLight(ChunkHolder chunk, int x, int y, int z) {
+ if (chunk.chunkSet.getLight() != null) {
+ int layer = y >> 4;
+ if (chunk.chunkSet.getLight()[layer] != null) {
+ int setLightValue = chunk.chunkSet.getLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
+ if (setLightValue < 16) {
+ return setLightValue;
+ }
+ }
+ }
+ return chunk.chunkExisting.getEmmittedLight(x, y, z);
+ }
+
+ @Override
+ public int getBrightness(ChunkHolder chunk, int x, int y, int z) {
+ return chunk.chunkExisting.getBrightness(x, y, z);
+ }
+
+ @Override
+ public int getOpacity(ChunkHolder chunk, int x, int y, int z) {
+ return chunk.chunkExisting.getOpacity(x, y, z);
+ }
};
private static final IBlockDelegate GET = new IBlockDelegate() {
@@ -185,6 +276,48 @@ public class ChunkHolder> implements IQueueChunk {
return chunk.setBlock(x, y, z, block);
}
+ @Override
+ public void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value) {
+ chunk.getOrCreateSet();
+ chunk.delegate = BOTH;
+ chunk.setSkyLight(x, y, z, value);
+ }
+
+ @Override
+ public void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value) {
+ chunk.getOrCreateSet();
+ chunk.delegate = BOTH;
+ chunk.setBlockLight(x, y, z, value);
+ }
+
+ @Override
+ public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
+ chunk.getOrCreateSet();
+ chunk.delegate = BOTH;
+ chunk.removeSectionLighting(layer, sky);
+ }
+
+ @Override
+ public void setFullBright(ChunkHolder chunk, int layer){
+ chunk.getOrCreateSet();
+ chunk.delegate = BOTH;
+ chunk.setFullBright(layer);
+ }
+
+ @Override
+ public void setLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
+ chunk.getOrCreateSet();
+ chunk.delegate = BOTH;
+ chunk.setLightLayer(layer, toSet);
+ }
+
+ @Override
+ public void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
+ chunk.getOrCreateSet();
+ chunk.delegate = BOTH;
+ chunk.setSkyLightLayer(layer, toSet);
+ }
+
@Override
public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getBiomeType(x, y, z);
@@ -200,6 +333,26 @@ public class ChunkHolder> implements IQueueChunk {
int z) {
return chunk.chunkExisting.getFullBlock(x, y, z);
}
+
+ @Override
+ public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
+ return chunk.chunkExisting.getSkyLight(x, y, z);
+ }
+
+ @Override
+ public int getEmmittedLight(ChunkHolder chunk, int x, int y, int z) {
+ return chunk.chunkExisting.getEmmittedLight(x, y, z);
+ }
+
+ @Override
+ public int getBrightness(ChunkHolder chunk, int x, int y, int z) {
+ return chunk.chunkExisting.getBrightness(x, y, z);
+ }
+
+ @Override
+ public int getOpacity(ChunkHolder chunk, int x, int y, int z) {
+ return chunk.chunkExisting.getOpacity(x, y, z);
+ }
};
private static final IBlockDelegate SET = new IBlockDelegate() {
@@ -226,6 +379,36 @@ public class ChunkHolder> implements IQueueChunk {
return chunk.chunkSet.setBlock(x, y, z, block);
}
+ @Override
+ public void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value) {
+ chunk.chunkSet.setSkyLight(x, y, z, value);
+ }
+
+ @Override
+ public void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value) {
+ chunk.chunkSet.setBlockLight(x, y, z, value);
+ }
+
+ @Override
+ public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
+ chunk.chunkSet.removeSectionLighting(layer, sky);
+ }
+
+ @Override
+ public void setFullBright(ChunkHolder chunk, int layer){
+ chunk.chunkSet.setFullBright(layer);
+ }
+
+ @Override
+ public void setLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
+ chunk.chunkSet.setLightLayer(layer, toSet);
+ }
+
+ @Override
+ public void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
+ chunk.chunkSet.setSkyLightLayer(layer, toSet);
+ }
+
@Override
public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet();
@@ -247,6 +430,52 @@ public class ChunkHolder> implements IQueueChunk {
chunk.delegate = BOTH;
return chunk.getFullBlock(x, y, z);
}
+
+ @Override
+ public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
+ if (chunk.chunkSet.getSkyLight() != null) {
+ int layer = y >> 4;
+ if (chunk.chunkSet.getSkyLight()[layer] != null) {
+ int setLightValue = chunk.chunkSet.getSkyLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
+ if (setLightValue < 16) {
+ return setLightValue;
+ }
+ }
+ }
+ chunk.getOrCreateGet();
+ chunk.delegate = BOTH;
+ return chunk.getSkyLight(x, y, z);
+ }
+
+ @Override
+ public int getEmmittedLight(ChunkHolder chunk, int x, int y, int z) {
+ if (chunk.chunkSet.getLight() != null) {
+ int layer = y >> 4;
+ if (chunk.chunkSet.getLight()[layer] != null) {
+ int setLightValue = chunk.chunkSet.getLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
+ if (setLightValue < 16) {
+ return setLightValue;
+ }
+ }
+ }
+ chunk.getOrCreateGet();
+ chunk.delegate = BOTH;
+ return chunk.getEmmittedLight(x, y, z);
+ }
+
+ @Override
+ public int getBrightness(ChunkHolder chunk, int x, int y, int z) {
+ chunk.getOrCreateGet();
+ chunk.delegate = BOTH;
+ return chunk.getBrightness(x, y, z);
+ }
+
+ @Override
+ public int getOpacity(ChunkHolder chunk, int x, int y, int z) {
+ chunk.getOrCreateGet();
+ chunk.delegate = BOTH;
+ return chunk.getOpacity(x, y, z);
+ }
};
private static final IBlockDelegate NULL = new IBlockDelegate() {
@@ -303,6 +532,80 @@ public class ChunkHolder> implements IQueueChunk {
chunk.chunkExisting.trim(false);
return chunk.getFullBlock(x, y, z);
}
+
+ @Override
+ public void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value) {
+ chunk.getOrCreateSet();
+ chunk.delegate = SET;
+ chunk.setSkyLight(x, y, z, value);
+ }
+
+ @Override
+ public void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value) {
+ chunk.getOrCreateSet();
+ chunk.delegate = SET;
+ chunk.setBlockLight(x, y, z, value);
+ }
+
+ @Override
+ public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
+ chunk.getOrCreateSet();
+ chunk.delegate = SET;
+ chunk.removeSectionLighting(layer, sky);
+ }
+
+ @Override
+ public void setFullBright(ChunkHolder chunk, int layer){
+ chunk.getOrCreateSet();
+ chunk.delegate = SET;
+ chunk.setFullBright(layer);
+ }
+
+ @Override
+ public void setLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
+ chunk.getOrCreateSet();
+ chunk.delegate = SET;
+ chunk.setLightLayer(layer, toSet);
+ }
+
+ @Override
+ public void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
+ chunk.getOrCreateSet();
+ chunk.delegate = SET;
+ chunk.setSkyLightLayer(layer, toSet);
+ }
+
+ @Override
+ public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
+ chunk.getOrCreateGet();
+ chunk.delegate = GET;
+ chunk.chunkExisting.trim(false);
+ return chunk.getSkyLight(x, y, z);
+ }
+
+ @Override
+ public int getEmmittedLight(ChunkHolder chunk, int x, int y, int z) {
+ chunk.getOrCreateGet();
+ chunk.delegate = GET;
+ chunk.chunkExisting.trim(false);
+ return chunk.getEmmittedLight(x, y, z);
+ }
+
+ @Override
+ public int getBrightness(ChunkHolder chunk, int x, int y, int z) {
+ chunk.getOrCreateGet();
+ chunk.delegate = GET;
+ chunk.chunkExisting.trim(false);
+ return chunk.getBrightness(x, y, z);
+ }
+
+ @Override
+ public int getOpacity(ChunkHolder chunk, int x, int y, int z) {
+ chunk.getOrCreateGet();
+ chunk.delegate = GET;
+ chunk.chunkExisting.trim(false);
+ return chunk.getOpacity(x, y, z);
+ }
};
@Override
@@ -416,11 +719,13 @@ public class ChunkHolder> implements IQueueChunk {
delegate = NULL;
}
chunkExisting = null;
+ isInit = true;
}
@Override
public synchronized T call() {
if (chunkSet != null) {
+ chunkSet.setBitMask(bitMask);
return this.call(chunkSet, this::recycle);
}
return null;
@@ -481,6 +786,54 @@ public class ChunkHolder> implements IQueueChunk {
return delegate.getFullBlock(this, x, y, z);
}
+ @Override
+ public void setSkyLight(int x, int y, int z, int value) {
+ delegate.setSkyLight(this, x, y, z, value);
+ }
+
+ @Override public void removeSectionLighting(int layer, boolean sky) {
+ delegate.removeSectionLighting(this, layer, sky);
+ }
+
+ @Override public void setFullBright(int layer) {
+ delegate.setFullBright(this, layer);
+ }
+
+ @Override
+ public void setBlockLight(int x, int y, int z, int value) {
+ delegate.setBlockLight(this, x, y, z, value);
+ }
+
+ @Override
+ public void setLightLayer(int layer, char[] toSet) {
+ delegate.setLightLayer(this, layer, toSet);
+ }
+
+ @Override
+ public void setSkyLightLayer(int layer, char[] toSet) {
+ delegate.setSkyLightLayer(this, layer, toSet);
+ }
+
+ @Override
+ public int getSkyLight(int x, int y, int z) {
+ return delegate.getSkyLight(this, x, y, z);
+ }
+
+ @Override
+ public int getEmmittedLight(int x, int y, int z) {
+ return delegate.getEmmittedLight(this, x, y, z);
+ }
+
+ @Override
+ public int getBrightness(int x, int y, int z) {
+ return delegate.getBrightness(this, x, y, z);
+ }
+
+ @Override
+ public int getOpacity(int x, int y, int z) {
+ return delegate.getOpacity(this, x, y, z);
+ }
+
public interface IBlockDelegate {
> IChunkGet get(ChunkHolder chunk);
IChunkSet set(ChunkHolder chunk);
@@ -494,5 +847,25 @@ public class ChunkHolder> implements IQueueChunk {
BlockState getBlock(ChunkHolder chunk, int x, int y, int z);
BaseBlock getFullBlock(ChunkHolder chunk, int x, int y, int z);
+
+ void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value);
+
+ void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value);
+
+ void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky);
+
+ void setFullBright(ChunkHolder chunk, int layer);
+
+ void setLightLayer(ChunkHolder chunk, int layer, char[] toSet);
+
+ void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet);
+
+ int getSkyLight(ChunkHolder chunk, int x, int y, int z);
+
+ int getEmmittedLight(ChunkHolder chunk, int x, int y, int z);
+
+ int getBrightness(ChunkHolder chunk, int x, int y, int z);
+
+ int getOpacity(ChunkHolder chunk, int x, int y, int z);
}
}
diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.kt b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.kt
index 6a6f33358..740b178ce 100644
--- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.kt
+++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.kt
@@ -62,6 +62,46 @@ object NullChunk : IQueueChunk {
return null
}
+ override fun getSkyLight(x: Int, y: Int, z: Int): Int {
+ return 15
+ }
+
+ override fun getLight(): Array {
+ return emptyArray()
+ }
+
+ override fun getSkyLight(): Array {
+ return emptyArray()
+ }
+
+ override fun getEmmittedLight(x: Int, y: Int, z: Int): Int {
+ return 15
+ }
+
+ override fun setSkyLight(x: Int, y: Int, z: Int, value: Int) {
+
+ }
+
+ override fun setBlockLight(x: Int, y: Int, z: Int, value: Int) {
+
+ }
+
+ override fun setFullBright(layer: Int) {
+
+ }
+
+ override fun removeSectionLighting(layer: Int, sky: Boolean) {
+
+ }
+
+ override fun setSkyLightLayer(layer: Int, toSet: CharArray?) {
+
+ }
+
+ override fun setLightLayer(layer: Int, toSet: CharArray?) {
+
+ }
+
override fun getBiomes(): Array? {
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
new file mode 100644
index 000000000..505ae5f20
--- /dev/null
+++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NMSRelighter.java
@@ -0,0 +1,645 @@
+package com.boydti.fawe.beta.implementation.lighting;
+
+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.RunnableVal;
+import com.boydti.fawe.object.collection.BlockVectorSet;
+import com.boydti.fawe.util.MathMan;
+import com.boydti.fawe.util.TaskManager;
+import com.sk89q.worldedit.math.MutableBlockVector3;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class NMSRelighter implements Relighter {
+ private final IQueueExtent queue;
+
+ private final Map skyToRelight;
+ private final Object present = new Object();
+ private final Map chunksToSend;
+ private final ConcurrentLinkedQueue extentdSkyToRelight = new ConcurrentLinkedQueue<>();
+
+ private final Map lightQueue;
+ private final AtomicBoolean lightLock = new AtomicBoolean(false);
+ private final ConcurrentHashMap concurrentLightQueue;
+
+ private final int maxY;
+
+ public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0);
+
+ private static final int DISPATCH_SIZE = 64;
+ private boolean removeFirst;
+
+ public NMSRelighter(IQueueExtent queue) {
+ this.queue = queue;
+ this.skyToRelight = new Long2ObjectOpenHashMap<>(12);
+ this.lightQueue = new Long2ObjectOpenHashMap<>(12);
+ this.chunksToSend = new Long2ObjectOpenHashMap<>(12);
+ this.concurrentLightQueue = new ConcurrentHashMap<>(12);
+ this.maxY = queue.getMaxY();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return skyToRelight.isEmpty() && lightQueue.isEmpty() && extentdSkyToRelight.isEmpty() && concurrentLightQueue.isEmpty();
+ }
+
+ @Override
+ public synchronized void removeAndRelight(boolean sky) {
+ removeFirst = true;
+ fixLightingSafe(sky);
+ removeFirst = false;
+ }
+
+ /**
+ * Utility method to reduce duplicated code to ensure values are written to long[][][] without NPEs
+ *
+ * @param x x coordinate
+ * @param y y coordinate
+ * @param z z coordinate
+ * @param map long[][][] to add values to
+ */
+ private void set(int x, int y, int z, long[][][] map) {
+ long[][] m1 = map[z];
+ if (m1 == null) {
+ m1 = map[z] = new long[16][];
+ }
+ long[] m2 = m1[x];
+ if (m2 == null) {
+ m2 = m1[x] = new long[4];
+ }
+ long value = m2[y >> 6] |= 1l << y;
+ }
+
+ public void addLightUpdate(int x, int y, int z) {
+ long index = MathMan.pairInt(x >> 4, z >> 4);
+ if (lightLock.compareAndSet(false, true)) {
+ synchronized (lightQueue) {
+ try {
+ long[][][] currentMap = lightQueue.get(index);
+ if (currentMap == null) {
+ currentMap = new long[16][][];
+ this.lightQueue.put(index, currentMap);
+ }
+ set(x & 15, y, z & 15, currentMap);
+ } finally {
+ lightLock.set(false);
+ }
+ }
+ } else {
+ long[][][] currentMap = concurrentLightQueue.get(index);
+ if (currentMap == null) {
+ currentMap = new long[16][][];
+ this.concurrentLightQueue.put(index, currentMap);
+ }
+ set(x & 15, y, z & 15, currentMap);
+ }
+ }
+
+ public synchronized void clear() {
+ extentdSkyToRelight.clear();
+ skyToRelight.clear();
+ chunksToSend.clear();
+ lightQueue.clear();
+ }
+
+ public boolean addChunk(int cx, int cz, byte[] fix, int bitmask) {
+ RelightSkyEntry toPut = new RelightSkyEntry(cx, cz, fix, bitmask);
+ extentdSkyToRelight.add(toPut);
+ return true;
+ }
+
+ private synchronized Map getSkyMap() {
+ RelightSkyEntry entry;
+ while ((entry = extentdSkyToRelight.poll()) != null) {
+ long pair = MathMan.pairInt(entry.x, entry.z);
+ RelightSkyEntry existing = skyToRelight.put(pair, entry);
+ if (existing != null) {
+ entry.bitmask |= existing.bitmask;
+ if (entry.fix != null) {
+ for (int i = 0; i < entry.fix.length; i++) {
+ entry.fix[i] &= existing.fix[i];
+ }
+ }
+ }
+ }
+ return skyToRelight;
+ }
+
+ public synchronized void removeLighting() {
+ Iterator> iter = getSkyMap().entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry entry = iter.next();
+ RelightSkyEntry chunk = entry.getValue();
+ long pair = entry.getKey();
+ Integer existing = chunksToSend.get(pair);
+ chunksToSend.put(pair, chunk.bitmask | (existing != null ? existing : 0));
+ ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z);
+ if (!iChunk.isInit()) {
+ iChunk.init(queue, chunk.x, chunk.z);
+ }
+ for (int i = 0; i < 16; i++) {
+ iChunk.removeSectionLighting(i, true);
+ }
+ iter.remove();
+ }
+ }
+
+ public void updateBlockLight(Map map) {
+ int size = map.size();
+ if (size == 0) {
+ return;
+ }
+ Queue lightPropagationQueue = new ArrayDeque<>(32);
+ Queue