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 c42b26872..8a75076a4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/FaweAPI.java @@ -381,8 +381,12 @@ public class FaweAPI { } } if (mode != RelightMode.NONE) { - relighter.fixSkyLighting(); - relighter.fixBlockLighting(); + if (Settings.IMP.LIGHTING.REMOVE_FIRST) { + relighter.removeAndRelight(true); + } else { + relighter.fixSkyLighting(); + relighter.fixBlockLighting(); + } } else { relighter.removeLighting(); } 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 15284cbbb..1c515fd60 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 @@ -9,6 +9,13 @@ 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 com.sk89q.worldedit.registry.state.DirectionalProperty; +import com.sk89q.worldedit.registry.state.EnumProperty; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.registry.BlockMaterial; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import java.util.ArrayDeque; @@ -25,22 +32,29 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; public class NMSRelighter implements Relighter { - private final IQueueExtent queue; + private static final int DISPATCH_SIZE = 64; + private static final DirectionalProperty stairDirection; + private static final EnumProperty stairHalf; + private static final EnumProperty stairShape; + private static final EnumProperty slabHalf; + static { + stairDirection = (DirectionalProperty) (Property) BlockTypes.SANDSTONE_STAIRS.getProperty("facing"); + stairHalf = (EnumProperty) (Property) BlockTypes.SANDSTONE_STAIRS.getProperty("half"); + stairShape = (EnumProperty) (Property) BlockTypes.SANDSTONE_STAIRS.getProperty("shape"); + slabHalf = (EnumProperty) (Property) BlockTypes.SANDSTONE_SLAB.getProperty("type"); + } + + public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0); + 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 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) { @@ -52,13 +66,11 @@ public class NMSRelighter implements Relighter { this.maxY = queue.getMaxY(); } - @Override - public boolean isEmpty() { + @Override public boolean isEmpty() { return skyToRelight.isEmpty() && lightQueue.isEmpty() && extentdSkyToRelight.isEmpty() && concurrentLightQueue.isEmpty(); } - @Override - public synchronized void removeAndRelight(boolean sky) { + @Override public synchronized void removeAndRelight(boolean sky) { removeFirst = true; fixLightingSafe(sky); removeFirst = false; @@ -67,9 +79,9 @@ public class NMSRelighter implements Relighter { /** * 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 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) { @@ -147,7 +159,7 @@ public class NMSRelighter implements Relighter { 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); + ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z); if (!iChunk.isInit()) { iChunk.init(queue, chunk.x, chunk.z); } @@ -168,6 +180,9 @@ public class NMSRelighter implements Relighter { Map visited = new HashMap<>(32); Map removalVisited = new HashMap<>(32); + // Make sure BlockTypes is initialised so we can check block characteristics later if needed + BlockTypes.STONE.getMaterial(); + Iterator> iter = map.entrySet().iterator(); while (iter.hasNext() && size-- > 0) { Map.Entry entry = iter.next(); @@ -177,16 +192,20 @@ public class NMSRelighter implements Relighter { int chunkZ = MathMan.unpairIntY(index); int bx = chunkX << 4; int bz = chunkZ << 4; - ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunkX, chunkZ); + ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunkX, chunkZ); if (!iChunk.isInit()) { iChunk.init(queue, chunkX, chunkZ); } for (int lz = 0; lz < blocks.length; lz++) { long[][] m1 = blocks[lz]; - if (m1 == null) continue; + if (m1 == null) { + continue; + } for (int lx = 0; lx < m1.length; lx++) { long[] m2 = m1[lx]; - if (m2 == null) continue; + if (m2 == null) { + continue; + } for (int i = 0; i < m2.length; i++) { int yStart = i << 6; long value = m2[i]; @@ -203,7 +222,7 @@ public class NMSRelighter implements Relighter { MutableBlockVector3 node = new MutableBlockVector3(x, y, z); if (newLevel < oldLevel) { removalVisited.put(node, present); - lightRemovalQueue.add(new Object[]{node, oldLevel}); + lightRemovalQueue.add(new Object[] {node, oldLevel}); } else { visited.put(node, present); lightPropagationQueue.add(node); @@ -223,43 +242,458 @@ public class NMSRelighter implements Relighter { MutableBlockVector3 node = (MutableBlockVector3) val[0]; int lightLevel = (int) val[1]; - this.computeRemoveBlockLight(node.getX() - 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); - this.computeRemoveBlockLight(node.getX() + 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); + this.computeRemoveBlockLight(node.getX() - 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, + removalVisited, visited); + this.computeRemoveBlockLight(node.getX() + 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, + removalVisited, visited); if (node.getY() > 0) { - this.computeRemoveBlockLight(node.getX(), node.getY() - 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); + this.computeRemoveBlockLight(node.getX(), node.getY() - 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, + removalVisited, visited); } if (node.getY() < 255) { - this.computeRemoveBlockLight(node.getX(), node.getY() + 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); + this.computeRemoveBlockLight(node.getX(), node.getY() + 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, + removalVisited, visited); } - this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() - 1, lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); - this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() + 1, lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited); + this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() - 1, lightLevel, lightRemovalQueue, lightPropagationQueue, + removalVisited, visited); + this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() + 1, lightLevel, lightRemovalQueue, lightPropagationQueue, + removalVisited, visited); } while (!lightPropagationQueue.isEmpty()) { MutableBlockVector3 node = lightPropagationQueue.poll(); - ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(node.getX() >> 4, node.getZ() >> 4); + ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(node.getX() >> 4, node.getZ() >> 4); if (!iChunk.isInit()) { iChunk.init(queue, node.getX() >> 4, node.getZ() >> 4); } int lightLevel = iChunk.getEmmittedLight(node.getX() & 15, node.getY(), node.getZ() & 15); - if (lightLevel > 1) { - this.computeSpreadBlockLight(node.getX() - 1, node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited); - this.computeSpreadBlockLight(node.getX() + 1, node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited); - if (node.getY() > 0) { - this.computeSpreadBlockLight(node.getX(), node.getY() - 1, node.getZ(), lightLevel, lightPropagationQueue, visited); - } - if (node.getY() < 255) { - this.computeSpreadBlockLight(node.getX(), node.getY() + 1, node.getZ(), lightLevel, lightPropagationQueue, visited); - } - this.computeSpreadBlockLight(node.getX(), node.getY(), node.getZ() - 1, lightLevel, lightPropagationQueue, visited); - this.computeSpreadBlockLight(node.getX(), node.getY(), node.getZ() + 1, lightLevel, lightPropagationQueue, visited); + BlockState state = this.queue.getBlock(node.getX(), node.getY(), node.getZ()); + String id = state.getBlockType().getId().toLowerCase(); + if (lightLevel <= 1) { + continue; + } + if (id.contains("slab")) { + boolean top = state.getState(slabHalf).equalsIgnoreCase("top"); + computeSlab(node.getX(), node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited, top); + } else if (id.contains("stair")) { + boolean top = state.getState(stairHalf).equalsIgnoreCase("top"); + Direction direction = getStairDir(state); + String shape = getStairShape(state); + computeStair(node.getX(), node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited, top, direction, shape); + } else { + computeNormal(node.getX(), node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited); } } } - private void computeRemoveBlockLight(int x, int y, int z, int currentLight, Queue queue, Queue spreadQueue, Map visited, - Map spreadVisited) { - ChunkHolder iChunk = (ChunkHolder) this.queue.getOrCreateChunk(x >> 4, z >> 4); + private void computeStair(int x, + int y, + int z, + int currentLight, + Queue queue, + Map visited, + boolean top, + Direction direction, + String shape) { + east: + { + // Block East + if (direction != Direction.WEST && !((direction == Direction.NORTH && !shape.equals("inner_left")) || (direction == Direction.SOUTH + && !shape.equals("inner_right")) || (direction == Direction.EAST && shape.contains("outer")))) { + break east; + } + BlockState state = this.queue.getBlock(x + 1, y, z); + if (!(checkStairEast(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom"))) { + break east; + } + if (!state.getBlockType().getId().toLowerCase().contains("stair")) { + this.computeSpreadBlockLight(x + 1, y, z, currentLight, queue, visited); + break east; + } + Direction otherDir = getStairDir(state); + String otherShape = getStairShape(state); + boolean b1 = + (otherDir == Direction.NORTH && !otherShape.equals("outer_right")) || (otherDir == Direction.EAST && otherShape.equals("inner_left")); + boolean b2 = + (otherDir == Direction.SOUTH && !otherShape.equals("outer_left")) || (otherDir == Direction.EAST && otherShape.equals("inner_right")); + switch (direction) { + case EAST: + if (shape.equals("outer_right") && b1) { + break east; + } else if (shape.equals("outer_left") && b2) { + break east; + } + break; + case WEST: + if (shape.equals("straight") || shape.contains("outer")) { + break; + } else if (shape.equals("inner_left") && b1) { + break east; + } else if (shape.equals("inner_right") && b2) { + break east; + } + break; + case SOUTH: + if (shape.equals("inner_left") || b1 || (otherDir == Direction.SOUTH && otherShape.equals("inner_right"))) { + break east; + } + break; + case NORTH: + if (shape.equals("inner_right") || b2 || (otherDir == Direction.NORTH && otherShape.equals("inner_left"))) { + break east; + } + break; + } + this.computeSpreadBlockLight(x + 1, y, z, currentLight, queue, visited); + } + west: + { + // Block West + if (direction != Direction.EAST && !((direction == Direction.SOUTH && !shape.equals("inner_left")) || (direction == Direction.NORTH + && !shape.equals("inner_right")) || (direction == Direction.WEST && shape.contains("outer")))) { + break west; + } + BlockState state = this.queue.getBlock(x - 1, y, z); + if (!(checkStairWest(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom"))) { + break west; + } + if (!state.getBlockType().getId().toLowerCase().contains("stair")) { + this.computeSpreadBlockLight(x - 1, y, z, currentLight, queue, visited); + break west; + } + Direction otherDir = getStairDir(state); + String otherShape = getStairShape(state); + boolean b1 = + (otherDir == Direction.SOUTH && !otherShape.equals("outer_right")) || (otherDir == Direction.WEST && otherShape.equals("inner_left")); + boolean b2 = + (otherDir == Direction.NORTH && !otherShape.equals("outer_left")) || (otherDir == Direction.WEST && otherShape.equals("inner_right")); + switch (direction) { + case WEST: + if (shape.equals("outer_right") && b1) { + break west; + } else if (shape.equals("outer_left") && b2) { + break west; + } + break; + case EAST: + if (shape.equals("straight") || shape.contains("outer")) { + break; + } else if (shape.equals("inner_left") && b1) { + break west; + } else if (shape.equals("inner_right") && b2) { + break west; + } + break; + case NORTH: + if (shape.equals("inner_left") || b1 || (otherDir == Direction.NORTH && otherShape.equals("inner_right"))) { + break west; + } + break; + case SOUTH: + if (shape.equals("inner_right") || b2 || (otherDir == Direction.SOUTH && otherShape.equals("inner_left"))) { + break west; + } + break; + } + this.computeSpreadBlockLight(x - 1, y, z, currentLight, queue, visited); + } + south: + { + // Block South + if (direction != Direction.NORTH && !((direction == Direction.WEST && !shape.equals("inner_left")) || (direction == Direction.EAST + && !shape.equals("inner_right")) || (direction == Direction.SOUTH && shape.contains("outer")))) { + break south; + } + BlockState state = this.queue.getBlock(x, y, z + 1); + if (!(checkStairSouth(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom"))) { + break south; + } + if (!state.getBlockType().getId().toLowerCase().contains("stair")) { + this.computeSpreadBlockLight(x, y, z + 1, currentLight, queue, visited); + break south; + } + Direction otherDir = getStairDir(state); + String otherShape = getStairShape(state); + boolean b1 = + (otherDir == Direction.EAST && !otherShape.equals("outer_right")) || (otherDir == Direction.SOUTH && otherShape.equals("inner_left")); + boolean b2 = + (otherDir == Direction.WEST && !otherShape.equals("outer_left")) || (otherDir == Direction.SOUTH && otherShape.equals("inner_right")); + switch (direction) { + case SOUTH: + if (shape.equals("outer_right") && b1) { + break south; + } else if (shape.equals("outer_left") && b2) { + break south; + } + break; + case NORTH: + if (shape.equals("straight") || shape.contains("outer")) { + break; + } else if (shape.equals("inner_left") && b1) { + break south; + } else if (shape.equals("inner_right") && b2) { + break south; + } + break; + case WEST: + if (shape.equals("inner_left") || b1 || (otherDir == Direction.WEST && otherShape.equals("inner_right"))) { + break south; + } + break; + case EAST: + if (shape.equals("inner_right") || b2 || (otherDir == Direction.EAST && otherShape.equals("inner_left"))) { + break south; + } + break; + } + this.computeSpreadBlockLight(x, y, z + 1, currentLight, queue, visited); + } + north: + { + // Block North + if (direction != Direction.SOUTH && !((direction == Direction.EAST && !shape.equals("inner_left")) || (direction == Direction.WEST + && !shape.equals("inner_right")) || (direction == Direction.NORTH && shape.contains("outer")))) { + break north; + } + BlockState state = this.queue.getBlock(x, y, z - 1); + if (!(checkStairNorth(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom"))) { + break north; + } + if (!state.getBlockType().getId().toLowerCase().contains("stair")) { + this.computeSpreadBlockLight(x, y, z - 1, currentLight, queue, visited); + break north; + } + Direction otherDir = getStairDir(state); + String otherShape = getStairShape(state); + boolean b1 = + (otherDir == Direction.WEST && !otherShape.equals("outer_right")) || (otherDir == Direction.NORTH && otherShape.equals("inner_left")); + boolean b2 = + (otherDir == Direction.EAST && !otherShape.equals("outer_left")) || (otherDir == Direction.NORTH && otherShape.equals("inner_right")); + switch (direction) { + case NORTH: + if (shape.equals("outer_right") && b1) { + break north; + } else if (shape.equals("outer_left") && b2) { + break north; + } + break; + case SOUTH: + if (shape.equals("straight") || shape.contains("outer")) { + break; + } else if (shape.equals("inner_left") && b1) { + break north; + } else if (shape.equals("inner_right") && b2) { + break north; + } + break; + case EAST: + if (shape.equals("inner_left") || b1 || (otherDir == Direction.EAST && otherShape.equals("inner_right"))) { + break north; + } + break; + case WEST: + if (shape.equals("inner_right") || b2 || (otherDir == Direction.WEST && otherShape.equals("inner_left"))) { + break north; + } + break; + } + this.computeSpreadBlockLight(x, y, z - 1, currentLight, queue, visited); + } + computeUpDown(x, y, z, currentLight, queue, visited, top); + + } + + private void computeSlab(int x, + int y, + int z, + int currentLight, + Queue queue, + Map visited, + boolean top) { + { + // Block East + BlockState state = this.queue.getBlock(x + 1, y, z); + if (checkStairEast(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom")) { + this.computeSpreadBlockLight(x + 1, y, z, currentLight, queue, visited); + } + } + { + // Block West + BlockState state = this.queue.getBlock(x - 1, y, z); + if (checkStairWest(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom")) { + this.computeSpreadBlockLight(x - 1, y, z, currentLight, queue, visited); + } + } + { + // Block South + BlockState state = this.queue.getBlock(x, y, z + 1); + if (checkStairSouth(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom")) { + this.computeSpreadBlockLight(x, y, z + 1, currentLight, queue, visited); + } + } + { + // Block North + BlockState state = this.queue.getBlock(x, y, z - 1); + if (checkStairNorth(state) && isStairOrTrueTop(state, top) && isSlabOrTrueValue(state, top ? "top" : "bottom")) { + this.computeSpreadBlockLight(x, y, z - 1, currentLight, queue, visited); + } + } + computeUpDown(x, y, z, currentLight, queue, visited, top); + } + + private void computeUpDown(int x, + int y, + int z, + int currentLight, + Queue queue, + Map visited, + boolean top) { + BlockState state = this.queue.getBlock(x, y - 1, z); + if (y > 0 && top && isSlabOrTrueValue(state, "bottom") && isStairOrTrueTop(state, false)) { + this.computeSpreadBlockLight(x, y - 1, z, currentLight, queue, visited); + } + state = this.queue.getBlock(x, y + 1, z); + if (y < 255 && !top && isSlabOrTrueValue(state, "top") && isStairOrTrueTop(state, true)) { + this.computeSpreadBlockLight(x, y + 1, z, currentLight, queue, visited); + } + } + + private void computeNormal(int x, int y, int z, int currentLight, Queue queue, Map visited) { + { + // Block East + BlockState state = this.queue.getBlock(x + 1, y, z); + if (checkStairEast(state) && (isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "bottom"))) { + this.computeSpreadBlockLight(x + 1, y, z, currentLight, queue, visited); + } + } + { + // Block West + BlockState state = this.queue.getBlock(x - 1, y, z); + if (checkStairWest(state) && (isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "bottom"))) { + this.computeSpreadBlockLight(x - 1, y, z, currentLight, queue, visited); + } + } + { + // Block South + BlockState state = this.queue.getBlock(x, y, z + 1); + if (checkStairSouth(state) && (isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "bottom"))) { + this.computeSpreadBlockLight(x, y, z + 1, currentLight, queue, visited); + } + } + { + // Block North + BlockState state = this.queue.getBlock(x, y, z - 1); + if (checkStairNorth(state) && (isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "bottom"))) { + this.computeSpreadBlockLight(x, y, z - 1, currentLight, queue, visited); + } + } + BlockState state = this.queue.getBlock(x, y - 1, z); + if (y > 0 && isSlabOrTrueValue(state, "bottom") && isStairOrTrueTop(state, false)) { + this.computeSpreadBlockLight(x, y - 1, z, currentLight, queue, visited); + } + state = this.queue.getBlock(x, y + 1, z); + if (y < 255 && isSlabOrTrueValue(state, "top") && isStairOrTrueTop(state, false)) { + this.computeSpreadBlockLight(x, y + 1, z, currentLight, queue, visited); + } + } + + private boolean checkStairNorth(BlockState state) { + if (!state.getBlockType().getId().toLowerCase().contains("stair")) { + return true; + } + Direction direction = getStairDir(state); + String shape = getStairShape(state); + if (shape.contains("outer") || direction == Direction.NORTH) { + return true; + } + if (direction == Direction.SOUTH) { + return false; + } + if (direction == Direction.WEST) { + return !shape.equals("inner_left"); + } + return direction != Direction.EAST || !shape.equals("inner_right"); + } + + private boolean checkStairSouth(BlockState state) { + if (!state.getBlockType().getId().toLowerCase().contains("stair")) { + return true; + } + Direction direction = getStairDir(state); + String shape = getStairShape(state); + if (shape.contains("outer") || direction == Direction.SOUTH) { + return true; + } + if (direction == Direction.NORTH) { + return false; + } + if (direction == Direction.EAST) { + return !shape.equals("inner_left"); + } + return direction != Direction.WEST || !shape.equals("inner_right"); + } + + private boolean checkStairEast(BlockState state) { + if (!state.getBlockType().getId().toLowerCase().contains("stair")) { + return true; + } + Direction direction = getStairDir(state); + String shape = getStairShape(state); + if (shape.contains("outer") || direction == Direction.EAST) { + return true; + } + if (direction == Direction.WEST) { + return false; + } + if (direction == Direction.NORTH) { + return !shape.equals("inner_left"); + } + return direction != Direction.SOUTH || !shape.equals("inner_right"); + } + + private boolean checkStairWest(BlockState state) { + if (!state.getBlockType().getId().toLowerCase().contains("stair")) { + return true; + } + Direction direction = getStairDir(state); + String shape = getStairShape(state); + if (shape.contains("outer") || direction == Direction.WEST) { + return true; + } + if (direction == Direction.EAST) { + return false; + } + if (direction == Direction.SOUTH) { + return !shape.equals("inner_left"); + } + return direction != Direction.NORTH || !shape.equals("inner_right"); + } + + private Direction getStairDir(BlockState state) { + return state.getState(stairDirection); + } + + private String getStairShape(BlockState state) { + return state.getState(stairShape).toLowerCase(); + } + + private boolean isStairOrTrueTop(BlockState state, boolean top) { + return !state.getBlockType().getId().contains("stair") || state.getState(stairHalf).equals("top") == top; + } + + private boolean isSlabOrTrueValue(BlockState state, String value) { + return !state.getBlockType().getId().contains("slab") || state.getState(slabHalf).equals(value); + } + + private void computeRemoveBlockLight(int x, + int y, + int z, + int currentLight, + Queue queue, + Queue spreadQueue, + Map visited, + Map spreadVisited) { + ChunkHolder iChunk = (ChunkHolder) this.queue.getOrCreateChunk(x >> 4, z >> 4); if (!iChunk.isInit()) { iChunk.init(this.queue, x >> 4, z >> 4); } @@ -270,7 +704,7 @@ public class NMSRelighter implements Relighter { if (!visited.containsKey(mutableBlockPos)) { MutableBlockVector3 index = new MutableBlockVector3(x, y, z); visited.put(index, present); - queue.add(new Object[]{index, current}); + queue.add(new Object[] {index, current}); } } } else if (current >= currentLight) { @@ -283,16 +717,23 @@ public class NMSRelighter implements Relighter { } } - private void computeSpreadBlockLight(int x, int y, int z, int currentLight, Queue queue, Map visited) { - currentLight = currentLight - Math.max(1, this.queue.getOpacity(x, y, z)); + private void computeSpreadBlockLight(int x, + int y, + int z, + int currentLight, + Queue queue, + Map visited) { + BlockMaterial material = this.queue.getBlock(x, y, z).getMaterial(); + boolean solidNeedsLight = (!material.isSolid() || !material.isFullCube()) && material.getLightOpacity() > 0 && material.getLightValue() == 0; + currentLight = !solidNeedsLight ? currentLight - Math.max(1, material.getLightOpacity()) : currentLight - 1; if (currentLight > 0) { - ChunkHolder iChunk = (ChunkHolder) this.queue.getOrCreateChunk(x >> 4, z >> 4); + ChunkHolder iChunk = (ChunkHolder) this.queue.getOrCreateChunk(x >> 4, z >> 4); if (!iChunk.isInit()) { iChunk.init(this.queue, x >> 4, z >> 4); } int current = iChunk.getEmmittedLight(x & 15, y, z & 15); - if (current < currentLight) { - iChunk.setBlockLight(x, y, z, currentLight); + if (currentLight > current) { + iChunk.setBlockLight(x & 15, y, z & 15, currentLight); mutableBlockPos.setComponents(x, y, z); if (!visited.containsKey(mutableBlockPos)) { visited.put(new MutableBlockVector3(x, y, z), present); @@ -305,7 +746,9 @@ public class NMSRelighter implements Relighter { } public void fixLightingSafe(boolean sky) { - if (isEmpty()) return; + if (isEmpty()) { + return; + } try { if (sky) { fixSkyLighting(); @@ -328,7 +771,8 @@ public class NMSRelighter implements Relighter { public void fixBlockLighting() { synchronized (lightQueue) { - while (!lightLock.compareAndSet(false, true)); + while (!lightLock.compareAndSet(false, true)) + ; try { updateBlockLight(this.lightQueue); } finally { @@ -345,7 +789,7 @@ public class NMSRelighter implements Relighter { int bitMask = entry.getValue(); int x = MathMan.unpairIntX(pair); int z = MathMan.unpairIntY(pair); - ChunkHolder chunk = (ChunkHolder) queue.getOrCreateChunk(x, z); + ChunkHolder chunk = (ChunkHolder) queue.getOrCreateChunk(x, z); chunk.setBitMask(bitMask); iter.remove(); } @@ -439,7 +883,7 @@ public class NMSRelighter implements Relighter { } int bx = chunk.x << 4; int bz = chunk.z << 4; - ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z); + ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z); if (!iChunk.isInit()) { iChunk.init(queue, chunk.x, chunk.z); } @@ -453,9 +897,10 @@ public class NMSRelighter implements Relighter { int x = j & 15; int z = j >> 4; byte value = mask[j]; - byte pair = MathMan.pair16(iChunk.getOpacity(x, y, z), iChunk.getBrightness(x, y, z)); - int opacity = MathMan.unpair16x(pair); - int brightness = MathMan.unpair16y(pair); + BlockMaterial material = iChunk.getBlock(x, y, z).getBlockType().getMaterial(); + int opacity = material.getLightOpacity(); + int brightness = iChunk.getBrightness(x, y, z); + boolean solidNeedsLight = (!material.isSolid() || !material.isFullCube()) && material.getLightOpacity() > 0; if (brightness > 1) { addLightUpdate(bx + x, y, bz + z); } @@ -482,7 +927,11 @@ public class NMSRelighter implements Relighter { case 14: if (opacity >= value) { mask[j] = 0; - iChunk.setSkyLight(x, y, z, 0); + if (solidNeedsLight) { + iChunk.setSkyLight(x, y, z, value); + } else { + iChunk.setSkyLight(x, y, z, 0); + } continue; } if (opacity <= 1) { @@ -496,7 +945,11 @@ public class NMSRelighter implements Relighter { value -= opacity; mask[j] = value; } - iChunk.setSkyLight(x, y, z, value); + if (solidNeedsLight) { + iChunk.setSkyLight(x, y, z, value + opacity); + } else { + iChunk.setSkyLight(x, y, z, value); + } continue; } chunk.smooth = true; @@ -519,15 +972,15 @@ public class NMSRelighter implements Relighter { public void smoothSkyLight(RelightSkyEntry chunk, int y, boolean direction) { byte[] mask = chunk.mask; - ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z); - ChunkHolder iChunkx; - ChunkHolder iChunkz; + ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z); + ChunkHolder iChunkx; + ChunkHolder iChunkz; if (!iChunk.isInit()) { iChunk.init(queue, chunk.x, chunk.z); } if (direction) { - iChunkx = (ChunkHolder) queue.getOrCreateChunk(chunk.x - 1, chunk.z); - iChunkz = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z - 1); + iChunkx = (ChunkHolder) queue.getOrCreateChunk(chunk.x - 1, chunk.z); + iChunkz = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z - 1); if (!iChunkx.isInit()) { iChunkx.init(queue, chunk.x - 1, chunk.z); } @@ -542,26 +995,38 @@ public class NMSRelighter implements Relighter { } byte value = mask[j]; if (x != 0 && z != 0) { - if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) ; - else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) ; - if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); + if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) { + } else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) { + } + if (value > mask[j]) { + iChunk.setSkyLight(x, y, z, mask[j] = value); + } } else if (x == 0 && z == 0) { - if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) ; - else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) ; - if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); + if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) { + } else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) { + } + if (value > mask[j]) { + iChunk.setSkyLight(x, y, z, mask[j] = value); + } } else if (x == 0) { - if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) ; - else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) ; - if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); + if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) { + } else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) { + } + if (value > mask[j]) { + iChunk.setSkyLight(x, y, z, mask[j] = value); + } } else { - if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) ; - else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) ; - if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); + if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) { + } else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) { + } + if (value > mask[j]) { + iChunk.setSkyLight(x, y, z, mask[j] = value); + } } } } else { - iChunkx = (ChunkHolder) queue.getOrCreateChunk(chunk.x + 1, chunk.z); - iChunkz = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z + 1); + iChunkx = (ChunkHolder) queue.getOrCreateChunk(chunk.x + 1, chunk.z); + iChunkz = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z + 1); if (!iChunkx.isInit()) { iChunkx.init(queue, chunk.x - 1, chunk.z); } @@ -575,22 +1040,34 @@ public class NMSRelighter implements Relighter { continue; } byte value = mask[j]; - if ( x != 15 && z != 15) { - if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) ; - else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) ; - if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); + if (x != 15 && z != 15) { + if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) { + } else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) { + } + if (value > mask[j]) { + iChunk.setSkyLight(x, y, z, mask[j] = value); + } } else if (x == 15 && z == 15) { - if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) ; - else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) ; - if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); + if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) { + } else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) { + } + if (value > mask[j]) { + iChunk.setSkyLight(x, y, z, mask[j] = value); + } } else if (x == 15) { - if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) ; - else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) ; - if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); + if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) { + } else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) { + } + if (value > mask[j]) { + iChunk.setSkyLight(x, y, z, mask[j] = value); + } } else { - if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) ; - else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) ; - if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value); + if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) { + } else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) { + } + if (value > mask[j]) { + iChunk.setSkyLight(x, y, z, mask[j] = value); + } } } } @@ -619,13 +1096,11 @@ public class NMSRelighter implements Relighter { } } - @Override - public String toString() { + @Override public String toString() { return x + "," + z; } - @Override - public int compareTo(RelightSkyEntry o) { + @Override public int compareTo(RelightSkyEntry o) { if (o.x < x) { return 1; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java index 5a3950846..5707e9d60 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java @@ -480,7 +480,7 @@ public class Settings extends Config { }) public int MODE = 1; @Comment({"If existing lighting should be removed before relighting"}) - public boolean REMOVE_FIRST = false; + public boolean REMOVE_FIRST = true; } public void reload(File file) {