Relight using starlight engine on Tuinity & perform heightmap updates (#1023)

* Relight using starlight engine on Tuinity

* Make use of invokeExact

* Cache MethodHandle

* Address some requested changes

* Remove random *

Co-authored-by: NotMyFault <mc.cache@web.de>

* Simplify and clean up sendChunk
Hopefully, that doesn't cause any issues

* Add naive HeightmapProcessor

* Make HeightmapProcessor more efficient

* Remove heightmap code from NMSRelighter

* Recognize fluid for waterlogged blocks

* Remove config option for heightmaps as they should always be updated

* Batch relighting for Starlight

* Dirty workaround for CharBlocks blocks NPE

* Revert "Dirty workaround for CharBlocks blocks NPE"

This reverts commit 737606a7
It only caused the heightmap to be wrong again and didn't help much with the original issue

* Adapt better chunk sending on older versions

* Adapt requested changes for HeightMapType

* Relight all changed chunks, batched
Also, address some requested changes

* Avoid deadlocks

* Clean up tuinity relighter and add some comments

* Minor changes to HeightmapProcessor

Co-authored-by: BuildTools <unconfigured@null.spigotmc.org>
Co-authored-by: NotMyFault <mc.cache@web.de>
Co-authored-by: Aurora <21148213+aurorasmiles@users.noreply.github.com>
This commit is contained in:
Hannes Greule
2021-05-13 17:29:11 +02:00
committed by GitHub
parent 46f1882496
commit ff728478c6
20 changed files with 653 additions and 263 deletions

View File

@ -2,7 +2,7 @@ package com.boydti.fawe;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.lighting.NMSRelighter;
import com.boydti.fawe.beta.implementation.lighting.Relighter;
import com.boydti.fawe.beta.implementation.queue.ParallelQueueExtent;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.RegionWrapper;
@ -29,12 +29,15 @@ import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.World;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.net.URL;
@ -45,7 +48,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
/**
* The FaweAPI class offers a few useful functions.<br>
@ -56,6 +58,8 @@ import javax.annotation.Nullable;
*/
public class FaweAPI {
private static final Logger LOGGER = LogManagerCompat.getLogger();
/**
* Offers a lot of options for building an EditSession.
*
@ -344,24 +348,29 @@ public class FaweAPI {
}
}
NMSRelighter relighter = new NMSRelighter(queue, Settings.IMP.LIGHTING.DO_HEIGHTMAPS);
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
relighter.addChunk(x, z, null, 65535);
count++;
try (Relighter relighter = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING)
.getRelighterFactory()
.createRelighter(mode, world, queue)) {
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
relighter.addChunk(x, z, null, 65535);
count++;
}
}
}
if (mode != RelightMode.NONE) {
if (Settings.IMP.LIGHTING.REMOVE_FIRST) {
relighter.removeAndRelight(true);
if (mode != RelightMode.NONE) {
if (Settings.IMP.LIGHTING.REMOVE_FIRST) {
relighter.removeAndRelight(true);
} else {
relighter.fixSkyLighting();
relighter.fixBlockLighting();
}
} else {
relighter.fixSkyLighting();
relighter.fixBlockLighting();
relighter.removeLighting();
}
} else {
relighter.removeLighting();
} catch (Exception e) {
LOGGER.error("Error occurred on fix lighting", e);
}
relighter.flush();
return count;
}

View File

@ -1,5 +1,68 @@
package com.boydti.fawe.beta.implementation.lighting;
import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.registry.state.PropertyKey;
import com.sk89q.worldedit.world.block.BlockCategories;
import com.sk89q.worldedit.world.block.BlockState;
/**
* This enum represents the different types of height maps available in minecraft.
*
* Heightmaps are used to describe the highest position for given {@code (x, z)} coordinates.
* What's considered as highest position depends on the height map type and the blocks at that column.
* The highest position is a {@code max(y + 1)} such that the block at {@code (x, y, z)} is
* {@link #includes(BlockState) included} by the height map type.
*/
public enum HeightMapType {
MOTION_BLOCKING, MOTION_BLOCKING_NO_LEAVES, OCEAN_FLOOR, WORLD_SURFACE
MOTION_BLOCKING {
@Override
public boolean includes(BlockState state) {
return state.getMaterial().isSolid() || HeightMapType.hasFluid(state);
}
},
MOTION_BLOCKING_NO_LEAVES {
@Override
public boolean includes(BlockState state) {
return (state.getMaterial().isSolid() || HeightMapType.hasFluid(state)) && !HeightMapType.isLeaf(state);
}
},
OCEAN_FLOOR {
@Override
public boolean includes(BlockState state) {
return state.getMaterial().isSolid();
}
},
WORLD_SURFACE {
@Override
public boolean includes(BlockState state) {
return !state.isAir();
}
};
private static boolean isLeaf(BlockState state) {
return BlockCategories.LEAVES.contains(state);
}
/**
* Returns whether the block state is a fluid or has an attribute that indicates the presence
* of fluid.
*
* @param state the block state to check.
* @return {@code true} if the block state has any fluid present.
*/
private static boolean hasFluid(BlockState state) {
if (state.getMaterial().isLiquid()) return true;
if (!state.getBlockType().hasProperty(PropertyKey.WATERLOGGED)) return false;
Property<Boolean> waterlogged = state.getBlockType().getProperty(PropertyKey.WATERLOGGED);
if (waterlogged == null) return false;
return state.getState(waterlogged);
}
/**
* Returns whether the given block state is included by this height map.
*
* @param state the block state to check.
* @return {@code true} if the block is included.
*/
public abstract boolean includes(BlockState state);
}

View File

@ -12,7 +12,6 @@ import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
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;
@ -37,7 +36,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
public class NMSRelighter implements Relighter {
@ -47,14 +45,12 @@ public class NMSRelighter implements Relighter {
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);
@ -62,31 +58,27 @@ public class NMSRelighter implements Relighter {
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 RelightMode relightMode;
private final int maxY;
private final boolean calculateHeightMaps;
private final ReentrantLock lightingLock;
private final AtomicBoolean finished = new AtomicBoolean(false);
private boolean removeFirst;
public NMSRelighter(IQueueExtent<IQueueChunk> queue, boolean calculateHeightMaps) {
this(queue, calculateHeightMaps, null);
this(queue, null);
}
public NMSRelighter(IQueueExtent<IQueueChunk> queue, boolean calculateHeightMaps, RelightMode relightMode) {
public NMSRelighter(IQueueExtent<IQueueChunk> queue, RelightMode relightMode) {
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;
this.relightMode = relightMode != null ? relightMode : RelightMode.valueOf(Settings.IMP.LIGHTING.MODE);
this.lightingLock = new ReentrantLock();
}
@ -160,12 +152,11 @@ public class NMSRelighter implements Relighter {
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);
RelightSkyEntry toPut = new RelightSkyEntry(cx, cz, fix, bitmask);
extentdSkyToRelight.add(toPut);
return true;
}
@ -823,7 +814,8 @@ public class NMSRelighter implements Relighter {
}
}
public synchronized void flush() {
@Override
public synchronized void close() {
Iterator<Map.Entry<Long, Integer>> iter = chunksToSend.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, Integer> entry = iter.next();
@ -833,14 +825,6 @@ public class NMSRelighter implements Relighter {
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) {
@ -856,6 +840,14 @@ public class NMSRelighter implements Relighter {
}
}
public void flush() {
try {
close();
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void sendChunks() {
RunnableVal<Object> runnable = new RunnableVal<Object>() {
@Override
@ -869,14 +861,6 @@ public class NMSRelighter implements Relighter {
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());
}
}
}
chunk.flushLightToGet(true);
Fawe.imp().getPlatformAdapter().sendChunk(chunk.getOrCreateGet(), bitMask, true);
iter.remove();
@ -943,33 +927,19 @@ public class NMSRelighter implements Relighter {
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) {
if (remove) {
BlockVectorSet tmpSet = new BlockVectorSet();
if (remove) {
chunkSet = new BlockVectorSet();
for (RelightSkyEntry chunk : chunks) {
tmpSet.add(chunk.x, 0, chunk.z);
}
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]);
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);
}
}
}
@ -995,12 +965,6 @@ public class NMSRelighter implements Relighter {
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;
@ -1013,38 +977,6 @@ public class NMSRelighter implements Relighter {
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;
}
Map<Property<?>, Object> states = state.getStates();
try {
if (heightMapList.get(HeightMapType.MOTION_BLOCKING)[j] == 0 && (material.isSolid() || material.isLiquid() || (
states.containsKey(waterLogged) && state.getState(waterLogged)))) {
heightMapList.get(HeightMapType.MOTION_BLOCKING)[j] = y + 1;
}
} catch (Exception ignored) {
LOGGER.debug("Error calculating waterlogged state for BlockState: " + state.getBlockType().getId() + ". States:");
LOGGER.debug(states.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining(", ", "{", "}")));
}
try {
if (heightMapList.get(HeightMapType.MOTION_BLOCKING_NO_LEAVES)[j] == 0 && (material.isSolid() || material.isLiquid() || (
states.containsKey(waterLogged) && state.getState(waterLogged))) && !state.getBlockType().getId()
.toLowerCase().contains("leaves")) {
heightMapList.get(HeightMapType.MOTION_BLOCKING_NO_LEAVES)[j] = y + 1;
}
} catch (Exception ignored) {
LOGGER.debug("Error calculating waterlogged state for BlockState: " + state.getBlockType().getId() + ". States:");
LOGGER.debug(states.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining(", ", "{", "}")));
}
}
switch (value) {
case 0:
if (opacity > 1) {
@ -1222,7 +1154,7 @@ public class NMSRelighter implements Relighter {
public int bitmask;
public boolean smooth;
public RelightSkyEntry(int x, int z, byte[] fix, int bitmask, boolean heightmaps) {
public RelightSkyEntry(int x, int z, byte[] fix, int bitmask) {
this.x = x;
this.z = z;
byte[] array = new byte[256];

View File

@ -58,4 +58,9 @@ public class NullRelighter implements Relighter {
public boolean isFinished() {
return true;
}
@Override
public void close() throws Exception {
}
}

View File

@ -2,7 +2,7 @@ package com.boydti.fawe.beta.implementation.lighting;
import java.util.concurrent.locks.ReentrantLock;
public interface Relighter {
public interface Relighter extends AutoCloseable {
/**
* Add a chunk to be relit when {@link Relighter#removeLighting} etc are called.

View File

@ -0,0 +1,29 @@
package com.boydti.fawe.beta.implementation.lighting;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.object.RelightMode;
import com.sk89q.worldedit.world.World;
import org.jetbrains.annotations.NotNull;
/**
* This abstracts the creation of {@link Relighter}s to allow more modular code.
*/
@FunctionalInterface
public interface RelighterFactory {
/**
* Create a new {@link Relighter} that can be used by a {@link RelightProcessor}.
* <p>
* Implementations are meant to configure an appropriate Relighter using the specified
* parameters. There are no guarantees about the returned objects other than being non-null.
* If no valid Relighter can be created, {@link NullRelighter#INSTANCE} should be returned.
*
* @param relightMode the relight mode to use during relighting.
* @param world the world in which relighting should be done.
* @param queue the queue extent to work with.
* @return a new Relighter instance with the specified settings.
*/
@NotNull
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue);
}

View File

@ -0,0 +1,103 @@
package com.boydti.fawe.beta.implementation.processors;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.lighting.HeightMapType;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import org.jetbrains.annotations.Nullable;
import java.util.BitSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
public class HeightmapProcessor implements IBatchProcessor {
private static final HeightMapType[] TYPES = HeightMapType.values();
private static final BlockType RESERVED = BlockTypes.__RESERVED__;
private static final int SECTION_SIDE_LENGTH = 16;
private static final int BLOCKS_PER_Y_LEVEL = SECTION_SIDE_LENGTH * SECTION_SIDE_LENGTH;
private final int maxY;
private final int minY;
public HeightmapProcessor(World world) {
this.maxY = world.getMaxY();
this.minY = world.getMinY();
}
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
// each heightmap gets one 16*16 array
int[][] heightmaps = new int[TYPES.length][BLOCKS_PER_Y_LEVEL];
BitSet[] updated = new BitSet[TYPES.length];
for (int i = 0; i < updated.length; i++) {
updated[i] = new BitSet(BLOCKS_PER_Y_LEVEL);
}
int skip = 0;
int allSkipped = (1 << TYPES.length) - 1; // lowest types.length bits are set
for (int y = maxY; y >= minY; y--) {
boolean hasSectionSet = set.hasSection(y >> 4);
boolean hasSectionGet = get.hasSection(y >> 4);
if (!(hasSectionSet || hasSectionGet)) {
y -= (SECTION_SIDE_LENGTH - 1); // - 1, as we do y-- in the loop head
continue;
}
for (int z = 0; z < SECTION_SIDE_LENGTH; z++) {
for (int x = 0; x < SECTION_SIDE_LENGTH; x++) {
BlockState block = null;
if (hasSectionSet) {
block = set.getBlock(x, y, z);
}
if (block == null || block.getBlockType() == RESERVED) {
if (!hasSectionGet) continue;
block = get.getBlock(x, y, z);
}
// fast skip if block isn't relevant for any height map
if (block.isAir()) continue;
for (int i = 0; i < TYPES.length; i++) {
if ((skip & (1 << i)) != 0) continue; // skip finished height map
HeightMapType type = TYPES[i];
int index = (z << 4) | x;
if (!updated[i].get(index) // ignore if that position was already set
&& type.includes(block)) {
heightmaps[i][index] = y + 1; // mc requires + 1
updated[i].set(index); // mark as updated
}
}
}
}
for (int i = 0; i < updated.length; i++) {
if ((skip & (1 << i)) == 0 // if already true, skip cardinality calculation
&& updated[i].cardinality() == BLOCKS_PER_Y_LEVEL) {
skip |= 1 << i;
}
}
if (skip != allSkipped) continue;
break; // all maps are processed
}
for (int i = 0; i < TYPES.length; i++) {
set.setHeightMap(TYPES[i], heightmaps[i]);
}
return set;
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return CompletableFuture.completedFuture(set);
}
@Override
public @Nullable Extent construct(Extent child) {
throw new UnsupportedOperationException("Processing only");
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.READING_SET_BLOCKS;
}
}

View File

@ -482,8 +482,6 @@ public class Settings extends Config {
public int MODE = 1;
@Comment({"If existing lighting should be removed before relighting"})
public boolean REMOVE_FIRST = true;
@Comment({"Calculate and set heightmaps when relighting"})
public boolean DO_HEIGHTMAPS = true;
}
public void reload(File file) {

View File

@ -4,10 +4,10 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.lighting.NMSRelighter;
import com.boydti.fawe.beta.implementation.lighting.NullRelighter;
import com.boydti.fawe.beta.implementation.lighting.RelightProcessor;
import com.boydti.fawe.beta.implementation.lighting.Relighter;
import com.boydti.fawe.beta.implementation.processors.HeightmapProcessor;
import com.boydti.fawe.beta.implementation.processors.LimitExtent;
import com.boydti.fawe.beta.implementation.queue.ParallelQueueExtent;
import com.boydti.fawe.config.Caption;
@ -34,7 +34,7 @@ import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.extent.EditSessionEvent;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
@ -42,14 +42,13 @@ import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.eventbus.EventBus;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.world.World;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.Locale;
import java.util.UUID;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
@ -401,12 +400,14 @@ public class EditSessionBuilder {
}
// There's no need to do lighting (and it'll also just be a pain to implement) if we're not placing chunks
if (placeChunks && ((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.IMP.LIGHTING.MODE > 0))) {
relighter = new NMSRelighter(queue, Settings.IMP.LIGHTING.DO_HEIGHTMAPS,
relightMode != null ? relightMode : RelightMode.valueOf(Settings.IMP.LIGHTING.MODE));
relighter = WorldEdit.getInstance().getPlatformManager()
.queryCapability(Capability.WORLD_EDITING)
.getRelighterFactory().createRelighter(relightMode, world, queue);
extent.addProcessor(new RelightProcessor(relighter));
} else {
relighter = NullRelighter.INSTANCE;
}
extent.addProcessor(new HeightmapProcessor(world));
if (limit != null && !limit.isUnlimited() && regionExtent != null) {
this.extent = new LimitExtent(regionExtent, limit);
} else if (limit != null && !limit.isUnlimited()) {

View File

@ -19,6 +19,7 @@
package com.sk89q.worldedit.extension.platform;
import com.boydti.fawe.beta.implementation.lighting.RelighterFactory;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.internal.util.NonAbstractForCompatibility;
@ -29,6 +30,7 @@ import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.registry.Registries;
import org.enginehub.piston.CommandManager;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Locale;
@ -206,4 +208,13 @@ public interface Platform extends Keyed {
* @return A set of supported side effects
*/
Set<SideEffect> getSupportedSideEffects();
/**
* Get the {@link RelighterFactory} that can be used to obtain
* {@link com.boydti.fawe.beta.implementation.lighting.Relighter}s.
*
* @return the relighter factory to be used.
*/
@NotNull
RelighterFactory getRelighterFactory();
}

View File

@ -123,7 +123,9 @@ public final class BlockCategories {
public static BlockCategory get(String id) {
BlockCategory entry = BlockCategory.REGISTRY.get(id);
if (entry == null) {
return new BlockCategory(id);
BlockCategory blockCategory = new BlockCategory(id);
blockCategory.load();
return blockCategory;
}
return entry;
}