1196 lines
53 KiB
Java
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;
|
|
}
|
|
}
|
|
}
|