Files
Plex-FAWE/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/lighting/NMSRelighter.java
Matt 96dcb95b7c Javadoc and Formatting fixes. (#619)
Javadoc and Formatting fixes.

Also, extremely minor code changes which have been tested.
This commit is only part one of two commits that aim to fix problems with formatting in our project. In part two I will modify the Google Java Style Guide (since it closely matches our code style) for our project so there is guidance on how to format and document. 

* Updated PlotSquared URL
* Removed plugin acronyms
* Fixed a typo
* Fixed grammar
* Use modern block id's
* Update YouTube video URL
2020-10-05 13:41:41 -04:00

1196 lines
53 KiB
Java

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 com.sk89q.worldedit.registry.state.BooleanProperty;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
public class NMSRelighter implements Relighter {
private static final Logger log = LoggerFactory.getLogger(NMSRelighter.class);
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;
private static final BooleanProperty waterLogged;
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");
waterLogged = (BooleanProperty) (Property<?>) BlockTypes.SANDSTONE_SLAB.getProperty("waterlogged");
}
public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0);
private final IQueueExtent<IQueueChunk> queue;
private final Map<Long, RelightSkyEntry> skyToRelight;
private final Object present = new Object();
private final Map<Long, Integer> chunksToSend;
private final Map<Long, Map<HeightMapType, int[]>> heightMaps;
private final ConcurrentLinkedQueue<RelightSkyEntry> extentdSkyToRelight = new ConcurrentLinkedQueue<>();
private final Map<Long, long[][][] /* z y x */> lightQueue;
private final AtomicBoolean lightLock = new AtomicBoolean(false);
private final ConcurrentHashMap<Long, long[][][]> concurrentLightQueue;
private final int maxY;
private final boolean calculateHeightMaps;
private boolean removeFirst;
public NMSRelighter(IQueueExtent<IQueueChunk> queue, boolean calculateHeightMaps) {
this.queue = queue;
this.skyToRelight = new Long2ObjectOpenHashMap<>(12);
this.lightQueue = new Long2ObjectOpenHashMap<>(12);
this.chunksToSend = new Long2ObjectOpenHashMap<>(12);
this.concurrentLightQueue = new ConcurrentHashMap<>(12);
this.heightMaps = new Long2ObjectOpenHashMap<>(12);
this.maxY = queue.getMaxY();
this.calculateHeightMaps = calculateHeightMaps;
}
@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];
}
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();
heightMaps.clear();
lightQueue.clear();
}
public boolean addChunk(int cx, int cz, byte[] fix, int bitmask) {
RelightSkyEntry toPut = new RelightSkyEntry(cx, cz, fix, bitmask, calculateHeightMaps);
extentdSkyToRelight.add(toPut);
return true;
}
private synchronized Map<Long, RelightSkyEntry> 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<Map.Entry<Long, RelightSkyEntry>> iter = getSkyMap().entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, RelightSkyEntry> 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<Long, long[][][]> map) {
int size = map.size();
if (size == 0) {
return;
}
Queue<MutableBlockVector3> lightPropagationQueue = new ArrayDeque<>(32);
Queue<Object[]> lightRemovalQueue = new ArrayDeque<>(32);
Map<MutableBlockVector3, Object> visited = new HashMap<>(32);
Map<MutableBlockVector3, Object> removalVisited = new HashMap<>(32);
// Make sure BlockTypes is initialised so we can check block characteristics later if needed
BlockTypes.STONE.getMaterial();
Iterator<Map.Entry<Long, long[][][]>> iter = map.entrySet().iterator();
while (iter.hasNext() && size-- > 0) {
Map.Entry<Long, long[][][]> entry = iter.next();
long index = entry.getKey();
long[][][] blocks = entry.getValue();
int chunkX = MathMan.unpairIntX(index);
int chunkZ = MathMan.unpairIntY(index);
int bx = chunkX << 4;
int bz = chunkZ << 4;
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;
}
for (int lx = 0; lx < m1.length; lx++) {
long[] m2 = m1[lx];
if (m2 == null) {
continue;
}
for (int i = 0; i < m2.length; i++) {
int yStart = i << 6;
long value = m2[i];
if (value != 0) {
for (int j = 0; j < 64; j++) {
if (((value >> j) & 1) == 1) {
int x = lx + bx;
int y = yStart + j;
int z = lz + bz;
int oldLevel = iChunk.getEmmittedLight(lx, y, lz);
int newLevel = iChunk.getBrightness(lx, y, lz);
if (oldLevel != newLevel) {
iChunk.setBlockLight(lx, y, lz, newLevel);
MutableBlockVector3 node = new MutableBlockVector3(x, y, z);
if (newLevel < oldLevel) {
removalVisited.put(node, present);
lightRemovalQueue.add(new Object[] {node, oldLevel});
} else {
visited.put(node, present);
lightPropagationQueue.add(node);
}
}
}
}
}
}
}
}
iter.remove();
}
while (!lightRemovalQueue.isEmpty()) {
Object[] val = lightRemovalQueue.poll();
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);
if (node.getY() > 0) {
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(), 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);
if (!iChunk.isInit()) {
iChunk.init(queue, node.getX() >> 4, node.getZ() >> 4);
}
int lightLevel = iChunk.getEmmittedLight(node.getX() & 15, node.getY(), node.getZ() & 15);
BlockState state = this.queue.getBlock(node.getX(), node.getY(), node.getZ());
String id = state.getBlockType().getId().toLowerCase(Locale.ROOT);
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 computeStair(int x,
int y,
int z,
int currentLight,
Queue<MutableBlockVector3> queue,
Map<MutableBlockVector3, Object> 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(Locale.ROOT).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(Locale.ROOT).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(Locale.ROOT).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(Locale.ROOT).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<MutableBlockVector3> queue,
Map<MutableBlockVector3, Object> 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<MutableBlockVector3> queue,
Map<MutableBlockVector3, Object> 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<MutableBlockVector3> queue, Map<MutableBlockVector3, Object> 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(Locale.ROOT).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(Locale.ROOT).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(Locale.ROOT).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(Locale.ROOT).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(Locale.ROOT);
}
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<Object[]> queue,
Queue<MutableBlockVector3> spreadQueue,
Map<MutableBlockVector3, Object> visited,
Map<MutableBlockVector3, Object> spreadVisited) {
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 != 0 && current < currentLight) {
iChunk.setBlockLight(x, y, z, 0);
if (current > 1) {
if (!visited.containsKey(mutableBlockPos)) {
MutableBlockVector3 index = new MutableBlockVector3(x, y, z);
visited.put(index, present);
queue.add(new Object[] {index, current});
}
}
} else if (current >= currentLight) {
mutableBlockPos.setComponents(x, y, z);
if (!spreadVisited.containsKey(mutableBlockPos)) {
MutableBlockVector3 index = new MutableBlockVector3(x, y, z);
spreadVisited.put(index, present);
spreadQueue.add(index);
}
}
}
private void computeSpreadBlockLight(int x,
int y,
int z,
int currentLight,
Queue<MutableBlockVector3> queue,
Map<MutableBlockVector3, Object> 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);
if (!iChunk.isInit()) {
iChunk.init(this.queue, x >> 4, z >> 4);
}
int current = iChunk.getEmmittedLight(x & 15, y, z & 15);
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);
if (currentLight > 1) {
queue.add(new MutableBlockVector3(x, y, z));
}
}
}
}
}
public void fixLightingSafe(boolean sky) {
if (isEmpty()) {
return;
}
try {
if (sky) {
fixSkyLighting();
} else {
synchronized (this) {
Map<Long, RelightSkyEntry> map = getSkyMap();
Iterator<Map.Entry<Long, RelightSkyEntry>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, RelightSkyEntry> entry = iter.next();
chunksToSend.put(entry.getKey(), entry.getValue().bitmask);
iter.remove();
}
}
}
fixBlockLighting();
} catch (Throwable e) {
e.printStackTrace();
}
}
public void fixBlockLighting() {
synchronized (lightQueue) {
while (!lightLock.compareAndSet(false, true)) {
;
}
try {
updateBlockLight(this.lightQueue);
} finally {
lightLock.set(false);
}
}
}
public synchronized void sendChunks() {
Iterator<Map.Entry<Long, Integer>> iter = chunksToSend.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, Integer> entry = iter.next();
long pair = entry.getKey();
int bitMask = entry.getValue();
int x = MathMan.unpairIntX(pair);
int z = MathMan.unpairIntY(pair);
ChunkHolder<?> chunk = (ChunkHolder<?>) queue.getOrCreateChunk(x, z);
chunk.setBitMask(bitMask);
if (calculateHeightMaps && heightMaps != null) {
Map<HeightMapType, int[]> heightMapList = heightMaps.get(pair);
if (heightMapList != null) {
for (Map.Entry<HeightMapType, int[]> heightMapEntry : heightMapList.entrySet()) {
chunk.setHeightMap(heightMapEntry.getKey(), heightMapEntry.getValue());
}
}
}
iter.remove();
}
if (Settings.IMP.LIGHTING.ASYNC) {
queue.flush();
} else {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override public void run(Object value) {
queue.flush();
}
});
}
}
public synchronized void fixSkyLighting() {
// Order chunks
Map<Long, RelightSkyEntry> map = getSkyMap();
ArrayList<RelightSkyEntry> chunksList = new ArrayList<>(map.size());
Iterator<Map.Entry<Long, RelightSkyEntry>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, RelightSkyEntry> entry = iter.next();
chunksToSend.put(entry.getKey(), entry.getValue().bitmask);
chunksList.add(entry.getValue());
iter.remove();
}
Collections.sort(chunksList);
int size = chunksList.size();
if (size > DISPATCH_SIZE) {
int amount = (size + DISPATCH_SIZE - 1) / DISPATCH_SIZE;
for (int i = 0; i < amount; i++) {
int start = i * DISPATCH_SIZE;
int end = Math.min(size, start + DISPATCH_SIZE);
List<RelightSkyEntry> sub = chunksList.subList(start, end);
fixSkyLighting(sub);
}
} else {
fixSkyLighting(chunksList);
}
}
public void fill(byte[] mask, int chunkX, int y, int chunkZ, byte reason) {
if (y >= 16) {
Arrays.fill(mask, (byte) 15);
return;
}
switch (reason) {
case SkipReason.SOLID: {
Arrays.fill(mask, (byte) 0);
return;
}
case SkipReason.AIR: {
int bx = chunkX << 4;
int bz = chunkZ << 4;
int index = 0;
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
mask[index++] = (byte) queue.getSkyLight(bx + x, y, bz + z);
}
}
}
}
}
private void fixSkyLighting(List<RelightSkyEntry> sorted) {
RelightSkyEntry[] chunks = sorted.toArray(new RelightSkyEntry[sorted.size()]);
boolean remove = this.removeFirst;
boolean heightMaps = this.calculateHeightMaps;
BlockVectorSet chunkSet = null;
if (remove || heightMaps) {
BlockVectorSet tmpSet = new BlockVectorSet();
if (remove) {
chunkSet = new BlockVectorSet();
for (RelightSkyEntry chunk : chunks) {
tmpSet.add(chunk.x, 0, chunk.z);
}
}
for (RelightSkyEntry chunk : chunks) {
if (remove) {
int x = chunk.x;
int z = chunk.z;
if (tmpSet.contains(x + 1, 0, z) && tmpSet.contains(x - 1, 0, z) && tmpSet.contains(x, 0, z + 1) && tmpSet
.contains(x, 0, z - 1)) {
chunkSet.add(x, 0, z);
}
}
if (heightMaps) {
long pair = MathMan.pairInt(chunk.x, chunk.z);
this.heightMaps.putIfAbsent(pair, new HashMap<>());
Map<HeightMapType, int[]> heightMapList = this.heightMaps.get(pair);
heightMapList.putIfAbsent(HeightMapType.WORLD_SURFACE, new int[256]);
heightMapList.putIfAbsent(HeightMapType.OCEAN_FLOOR, new int[256]);
heightMapList.putIfAbsent(HeightMapType.MOTION_BLOCKING, new int[256]);
heightMapList.putIfAbsent(HeightMapType.MOTION_BLOCKING_NO_LEAVES, new int[256]);
}
}
}
for (int y = 255; y > 0; y--) {
for (RelightSkyEntry chunk : chunks) { // Propagate skylight
int layer = y >> 4;
byte[] mask = chunk.mask;
if ((y & 15) == 15 && chunk.fix[layer] != SkipReason.NONE) {
if ((y & 15) == 0 && layer != 0 && chunk.fix[layer - 1] == SkipReason.NONE) {
fill(mask, chunk.x, y, chunk.z, chunk.fix[layer]);
}
continue;
}
int bx = chunk.x << 4;
int bz = chunk.z << 4;
ChunkHolder<?> iChunk = (ChunkHolder<?>) queue.getOrCreateChunk(chunk.x, chunk.z);
if (!iChunk.isInit()) {
iChunk.init(queue, chunk.x, chunk.z);
}
chunk.smooth = false;
if (remove && (y & 15) == 15 && chunkSet.contains(chunk.x, 0, chunk.z)) {
iChunk.removeSectionLighting(y >> 4, true);
}
Map<HeightMapType, int[]> heightMapList = null;
if (heightMaps) {
long pair = MathMan.pairInt(chunk.x, chunk.z);
heightMapList = this.heightMaps.get(pair);
}
for (int j = 0; j < 256; j++) {
int x = j & 15;
int z = j >> 4;
byte value = mask[j];
BlockState state = iChunk.getBlock(x, y, z);
BlockMaterial material = state.getMaterial();
int opacity = material.getLightOpacity();
int brightness = material.getLightValue();
if (brightness > 1) {
addLightUpdate(bx + x, y, bz + z);
}
if (heightMaps) {
if (heightMapList.get(HeightMapType.WORLD_SURFACE)[j] == 0 && !material.isAir()) {
// MC Requires y+1
heightMapList.get(HeightMapType.WORLD_SURFACE)[j] = y + 1;
}
if (heightMapList.get(HeightMapType.OCEAN_FLOOR)[j] == 0 && material.isSolid()) {
heightMapList.get(HeightMapType.OCEAN_FLOOR)[j] = y + 1;
}
try {
if (heightMapList.get(HeightMapType.MOTION_BLOCKING)[j] == 0 && (material.isSolid() || material.isLiquid() || (
state.getStates().containsKey(waterLogged) && state.getState(waterLogged)))) {
heightMapList.get(HeightMapType.MOTION_BLOCKING)[j] = y + 1;
}
} catch (Exception ignored) {
log.debug("Error calculating waterlogged state for BlockState: " + state.getBlockType().getId() + ". States:");
log.debug(state.getStates().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining(", ", "{", "}")));
}
try {
if (heightMapList.get(HeightMapType.MOTION_BLOCKING_NO_LEAVES)[j] == 0 && (material.isSolid() || material.isLiquid() || (
state.getStates().containsKey(waterLogged) && state.getState(waterLogged))) && !state.getBlockType().getId()
.toLowerCase().contains("leaves")) {
heightMapList.get(HeightMapType.MOTION_BLOCKING_NO_LEAVES)[j] = y + 1;
}
} catch (Exception ignored) {
log.debug("Error calculating waterlogged state for BlockState: " + state.getBlockType().getId() + ". States:");
log.debug(state.getStates().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining(", ", "{", "}")));
}
}
switch (value) {
case 0:
if (opacity > 1) {
iChunk.setSkyLight(x, y, z, 0);
continue;
}
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
if (opacity >= value) {
mask[j] = 0;
if (!isStairOrTrueTop(state, true) || !(isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "double"))) {
iChunk.setSkyLight(x, y, z, value);
} else {
iChunk.setSkyLight(x, y, z, 0);
}
continue;
}
if (opacity <= 1) {
mask[j] = --value;
} else {
mask[j] = value = (byte) Math.max(0, value - opacity);
}
break;
case 15:
if (opacity > 0) {
value -= opacity;
mask[j] = value;
}
if (!isStairOrTrueTop(state, true) || !(isSlabOrTrueValue(state, "top") || isSlabOrTrueValue(state, "double"))) {
iChunk.setSkyLight(x, y, z, value + opacity);
} else {
iChunk.setSkyLight(x, y, z, value);
}
continue;
}
chunk.smooth = true;
iChunk.setSkyLight(x, y, z, value);
}
}
for (RelightSkyEntry chunk : chunks) { // Smooth forwards
if (chunk.smooth) {
smoothSkyLight(chunk, y, true);
}
}
for (int i = chunks.length - 1; i >= 0; i--) { // Smooth backwards
RelightSkyEntry chunk = chunks[i];
if (chunk.smooth) {
smoothSkyLight(chunk, y, false);
}
}
}
}
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;
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);
if (!iChunkx.isInit()) {
iChunkx.init(queue, chunk.x - 1, chunk.z);
}
if (!iChunkz.isInit()) {
iChunkz.init(queue, chunk.x, chunk.z - 1);
}
for (int j = 0; j < 256; j++) {
int x = j & 15;
int z = j >> 4;
if (mask[j] >= 14 || (mask[j] == 0 && iChunk.getOpacity(x, y, z) > 1)) {
continue;
}
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);
}
} 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);
}
} 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);
}
} 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);
}
}
}
} else {
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);
}
if (!iChunkz.isInit()) {
iChunkz.init(queue, chunk.x, chunk.z - 1);
}
for (int j = 255; j >= 0; j--) {
int x = j & 15;
int z = j >> 4;
if (mask[j] >= 14 || (mask[j] == 0 && iChunk.getOpacity(x, y, z) > 1)) {
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);
}
} 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);
}
} 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);
}
} 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);
}
}
}
}
}
private class RelightSkyEntry implements Comparable<RelightSkyEntry> {
public final int x;
public final int z;
public final byte[] mask;
public final byte[] fix;
public int bitmask;
public boolean smooth;
public RelightSkyEntry(int x, int z, byte[] fix, int bitmask, boolean heightmaps) {
this.x = x;
this.z = z;
byte[] array = new byte[256];
Arrays.fill(array, (byte) 15);
this.mask = array;
this.bitmask = bitmask;
if (fix == null) {
this.fix = new byte[(maxY + 1) >> 4];
Arrays.fill(this.fix, SkipReason.NONE);
} else {
this.fix = fix;
}
}
@Override public String toString() {
return x + "," + z;
}
@Override public int compareTo(RelightSkyEntry o) {
if (o.x < x) {
return 1;
}
if (o.x > x) {
return -1;
}
if (o.z < z) {
return 1;
}
if (o.z > z) {
return -1;
}
return 0;
}
}
}