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

@ -0,0 +1,19 @@
package com.boydti.fawe.bukkit;
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.lighting.RelighterFactory;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.RelightMode;
import com.sk89q.worldedit.world.World;
import org.jetbrains.annotations.NotNull;
public class NMSRelighterFactory implements RelighterFactory {
@Override
public @NotNull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
return new NMSRelighter(queue,
relightMode != null ? relightMode : RelightMode.valueOf(Settings.IMP.LIGHTING.MODE));
}
}

View File

@ -9,6 +9,7 @@ import com.boydti.fawe.object.collection.BitArray;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -29,6 +30,7 @@ import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.LightEngineStorage;
import net.minecraft.server.v1_15_R1.NibbleArray;
import net.minecraft.server.v1_15_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_15_R1.PacketPlayOutMapChunk;
import net.minecraft.server.v1_15_R1.PlayerChunk;
import net.minecraft.server.v1_15_R1.PlayerChunkMap;
import net.minecraft.server.v1_15_R1.World;
@ -44,6 +46,7 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
@ -189,32 +192,23 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
if (playerChunk == null) {
return;
}
if (playerChunk.hasBeenLoaded()) {
try {
int dirtyBits = fieldDirtyBits.getInt(playerChunk);
if (dirtyBits == 0) {
nmsWorld.getChunkProvider().playerChunkMap.a(playerChunk);
}
if (mask == 0) {
dirtyBits = 65535;
} else {
dirtyBits |= mask;
}
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
if (lighting) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(chunkX, chunkZ);
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(chunkX, chunkZ);
Optional<Chunk> optional = ((Either) playerChunk.a().getNow(PlayerChunk.UNLOADED_CHUNK)).left();
Chunk chunk = optional.orElseGet(() ->
nmsWorld.getChunkProvider().getChunkAtIfLoadedImmediately(chunkX, chunkZ));
if (chunk == null) {
return;
}
PacketPlayOutMapChunk chunkPacket = new PacketPlayOutMapChunk(chunk, 65535);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(chunkPacket);
});
if (lighting) {
PacketPlayOutLightUpdate packet =
new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
}

View File

@ -9,6 +9,7 @@ import com.boydti.fawe.object.collection.BitArrayUnstretched;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
@ -27,6 +28,7 @@ import net.minecraft.server.v1_16_R1.DataPaletteLinear;
import net.minecraft.server.v1_16_R1.GameProfileSerializer;
import net.minecraft.server.v1_16_R1.IBlockData;
import net.minecraft.server.v1_16_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_16_R1.PacketPlayOutMapChunk;
import net.minecraft.server.v1_16_R1.PlayerChunk;
import net.minecraft.server.v1_16_R1.PlayerChunkMap;
import net.minecraft.server.v1_16_R1.World;
@ -42,6 +44,7 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
@ -186,33 +189,26 @@ public final class BukkitAdapter_1_16_1 extends NMSAdapter {
if (playerChunk == null) {
return;
}
if (playerChunk.hasBeenLoaded()) {
try {
int dirtyBits = fieldDirtyBits.getInt(playerChunk);
if (dirtyBits == 0) {
nmsWorld.getChunkProvider().playerChunkMap.a(playerChunk);
}
if (mask == 0) {
dirtyBits = 65535;
} else {
dirtyBits |= mask;
}
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
if (lighting) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(chunkX, chunkZ);
boolean trustEdges = false; //Added in 1.16.1 Not sure what it does.
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(), trustEdges);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(chunkX, chunkZ);
Optional<Chunk> optional = ((Either) playerChunk.a().getNow(PlayerChunk.UNLOADED_CHUNK)).left();
Chunk chunk = optional.orElseGet(() ->
nmsWorld.getChunkProvider().getChunkAtIfLoadedImmediately(chunkX, chunkZ));
if (chunk == null) {
return;
}
PacketPlayOutMapChunk chunkPacket = new PacketPlayOutMapChunk(chunk, 65535, true);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(chunkPacket);
});
if (lighting) {
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
boolean trustEdges = true;
PacketPlayOutLightUpdate packet =
new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(),
trustEdges);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
}

View File

@ -9,7 +9,6 @@ import com.boydti.fawe.object.collection.BitArrayUnstretched;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.ReflectionUtils;
import com.boydti.fawe.util.TaskManager;
import com.destroystokyo.paper.util.misc.PooledLinkedHashSets;
import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockState;
@ -26,7 +25,6 @@ import net.minecraft.server.v1_16_R2.DataBits;
import net.minecraft.server.v1_16_R2.DataPalette;
import net.minecraft.server.v1_16_R2.DataPaletteBlock;
import net.minecraft.server.v1_16_R2.DataPaletteLinear;
import net.minecraft.server.v1_16_R2.EntityPlayer;
import net.minecraft.server.v1_16_R2.GameProfileSerializer;
import net.minecraft.server.v1_16_R2.IBlockData;
import net.minecraft.server.v1_16_R2.PacketPlayOutLightUpdate;
@ -198,50 +196,26 @@ public final class BukkitAdapter_1_16_2 extends NMSAdapter {
if (playerChunk == null) {
return;
}
if (playerChunk.hasBeenLoaded()) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(chunkX, chunkZ);
Optional<Chunk> optional = ((Either) playerChunk.a().getNow(PlayerChunk.UNLOADED_CHUNK)).left();
if (optional.isPresent()) {
PacketPlayOutMapChunk chunkpacket = new PacketPlayOutMapChunk(optional.get(), 65535);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(chunkpacket);
});
if (lighting) {
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
boolean trustEdges = true;
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(), trustEdges);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} else if (PaperLib.isPaper()) {
//Require generic here to work with multiple dependencies trying to take control.
PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<?> objects =
nmsWorld.getChunkProvider().playerChunkMap.playerViewDistanceNoTickMap.getObjectsInRange(chunkX, chunkZ);
if (objects == null) {
return;
}
for (Object obj : objects.getBackingSet()) {
if (obj == null) {
continue;
}
EntityPlayer p = (EntityPlayer) obj;
Chunk chunk = nmsWorld.getChunkProvider().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
if (chunk != null) {
PacketPlayOutMapChunk chunkpacket = new PacketPlayOutMapChunk(chunk, 65535);
p.playerConnection.sendPacket(chunkpacket);
if (lighting) {
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
boolean trustEdges = true;
PacketPlayOutLightUpdate packet =
new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(), trustEdges);
p.playerConnection.sendPacket(packet);
}
}
}
}
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(chunkX, chunkZ);
Optional<Chunk> optional = ((Either) playerChunk.a().getNow(PlayerChunk.UNLOADED_CHUNK)).left();
Chunk chunk = optional.orElseGet(() ->
nmsWorld.getChunkProvider().getChunkAtIfLoadedImmediately(chunkX, chunkZ));
if (chunk == null) {
return;
}
PacketPlayOutMapChunk chunkPacket = new PacketPlayOutMapChunk(chunk, 65535);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(chunkPacket);
});
if (lighting) {
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
boolean trustEdges = true;
PacketPlayOutLightUpdate packet =
new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(),
trustEdges);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
}

View File

@ -198,53 +198,26 @@ public final class BukkitAdapter_1_16_5 extends NMSAdapter {
if (playerChunk == null) {
return;
}
if (playerChunk.hasBeenLoaded()) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(chunkX, chunkZ);
Optional<Chunk> optional = ((Either) playerChunk.a().getNow(PlayerChunk.UNLOADED_CHUNK)).left();
if (optional.isPresent()) {
PacketPlayOutMapChunk chunkpacket = new PacketPlayOutMapChunk(optional.get(), 65535);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(chunkpacket);
});
if (lighting) {
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
boolean trustEdges = true;
PacketPlayOutLightUpdate packet =
new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(),
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(chunkX, chunkZ);
Optional<Chunk> optional = ((Either) playerChunk.a().getNow(PlayerChunk.UNLOADED_CHUNK)).left();
Chunk chunk = optional.orElseGet(() ->
nmsWorld.getChunkProvider().getChunkAtIfLoadedImmediately(chunkX, chunkZ));
if (chunk == null) {
return;
}
PacketPlayOutMapChunk chunkPacket = new PacketPlayOutMapChunk(chunk, 65535);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(chunkPacket);
});
if (lighting) {
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
boolean trustEdges = true;
PacketPlayOutLightUpdate packet =
new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine(),
trustEdges);
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} else if (PaperLib.isPaper()) {
//Require generic here to work with multiple dependencies trying to take control.
PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<?> objects =
nmsWorld.getChunkProvider().playerChunkMap.playerViewDistanceNoTickMap
.getObjectsInRange(chunkX, chunkZ);
if (objects == null) {
return;
}
for (Object obj : objects.getBackingSet()) {
if (obj == null) {
continue;
}
EntityPlayer p = (EntityPlayer) obj;
Chunk chunk = nmsWorld.getChunkProvider().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
if (chunk != null) {
PacketPlayOutMapChunk chunkpacket = new PacketPlayOutMapChunk(chunk, 65535);
p.playerConnection.sendPacket(chunkpacket);
if (lighting) {
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
boolean trustEdges = true;
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair,
nmsWorld.getChunkProvider().getLightEngine(), trustEdges);
p.playerConnection.sendPacket(packet);
}
}
}
}
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
}

View File

@ -0,0 +1,21 @@
package com.boydti.fawe.bukkit.adapter.mc1_16_5;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.lighting.NullRelighter;
import com.boydti.fawe.beta.implementation.lighting.Relighter;
import com.boydti.fawe.beta.implementation.lighting.RelighterFactory;
import com.boydti.fawe.object.RelightMode;
import com.sk89q.worldedit.world.World;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
import org.jetbrains.annotations.NotNull;
public class TuinityRelighterFactory_1_16_5 implements RelighterFactory {
@Override
public @NotNull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
org.bukkit.World w = Bukkit.getWorld(world.getName());
if (w == null) return NullRelighter.INSTANCE;
return new TuinityRelighter_1_16_5(((CraftWorld) w).getHandle(), queue);
}
}

View File

@ -0,0 +1,228 @@
package com.boydti.fawe.bukkit.adapter.mc1_16_5;
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.config.Settings;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.server.v1_16_R3.ChunkCoordIntPair;
import net.minecraft.server.v1_16_R3.ChunkStatus;
import net.minecraft.server.v1_16_R3.LightEngineThreaded;
import net.minecraft.server.v1_16_R3.MCUtil;
import net.minecraft.server.v1_16_R3.TicketType;
import net.minecraft.server.v1_16_R3.Unit;
import net.minecraft.server.v1_16_R3.WorldServer;
import org.apache.logging.log4j.Logger;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
public class TuinityRelighter_1_16_5 implements Relighter {
private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final MethodHandle RELIGHT;
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
private static final int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
private static final TicketType<Unit> FAWE_TICKET = TicketType.a("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
private final WorldServer world;
private final ReentrantLock lock = new ReentrantLock();
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
private final ReentrantLock areaLock = new ReentrantLock();
private final NMSRelighter delegate;
static {
MethodHandle tmp = null;
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
tmp = lookup.findVirtual(LightEngineThreaded.class,
"relight",
MethodType.methodType(
int.class, // return type
// params
Set.class,
Consumer.class,
IntConsumer.class
)
);
} catch (NoSuchMethodException | IllegalAccessException e) {
LOGGER.error("Failed to locate relight method in LightEngineThreaded on Tuinity. " +
"Is everything up to date?", e);
}
RELIGHT = tmp;
}
public TuinityRelighter_1_16_5(WorldServer world, IQueueExtent<IQueueChunk> queue) {
this.world = world;
this.delegate = new NMSRelighter(queue, false);
}
@Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
areaLock.lock();
try {
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
chunks.add(ChunkCoordIntPair.pair(cx, cz));
} finally {
areaLock.unlock();
}
return true;
}
@Override
public void addLightUpdate(int x, int y, int z) {
delegate.addLightUpdate(x, y, z);
}
/*
* This method is called "recursively", iterating and removing elements
* from the regions linked map. This way, chunks are loaded in batches to avoid
* OOMEs.
*/
@Override
public void fixLightingSafe(boolean sky) {
this.areaLock.lock();
try {
if (regions.isEmpty()) return;
LongSet first = regions.removeFirst();
fixLighting(first, () -> fixLightingSafe(true));
} finally {
this.areaLock.unlock();
}
}
/*
* Processes a set of chunks and runs an action afterwards.
* The action is run async, the chunks are partly processed on the main thread
* (as required by the server).
*/
private void fixLighting(LongSet chunks, Runnable andThen) {
// convert from long keys to ChunkCoordIntPairs
Set<ChunkCoordIntPair> coords = new HashSet<>();
LongIterator iterator = chunks.iterator();
while (iterator.hasNext()) {
coords.add(new ChunkCoordIntPair(iterator.nextLong()));
}
TaskManager.IMP.task(() -> {
// trigger chunk load and apply ticket on main thread
List<CompletableFuture<?>> futures = new ArrayList<>();
for (ChunkCoordIntPair pos : coords) {
futures.add(world.getWorld().getChunkAtAsync(pos.x, pos.z)
.thenAccept(c -> world.getChunkProvider().addTicketAtLevel(
FAWE_TICKET,
pos,
LIGHT_LEVEL,
Unit.INSTANCE))
);
}
// collect futures and trigger relight once all chunks are loaded
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
invokeRelight(coords,
c -> { }, // no callback for single chunks required
i -> {
if (i != coords.size()) {
LOGGER.warn("Processed " + i + " chunks instead of " + coords.size());
}
// post process chunks on main thread
TaskManager.IMP.task(() -> postProcessChunks(coords));
// call callback on our own threads
TaskManager.IMP.async(andThen);
})
);
});
}
private void invokeRelight(Set<ChunkCoordIntPair> coords,
Consumer<ChunkCoordIntPair> chunkCallback,
IntConsumer processCallback) {
try {
int unused = (int) RELIGHT.invokeExact(world.getChunkProvider().getLightEngine(),
coords,
chunkCallback, // callback per chunk
processCallback // callback for all chunks
);
} catch (Throwable throwable) {
LOGGER.error("Error occurred on relighting", throwable);
}
}
/*
* Allow the server to unload the chunks again.
* Also, if chunk packets are sent delayed, we need to do that here
*/
private void postProcessChunks(Set<ChunkCoordIntPair> coords) {
boolean delay = Settings.IMP.LIGHTING.DELAY_PACKET_SENDING;
for (ChunkCoordIntPair pos : coords) {
int x = pos.x;
int z = pos.z;
if (delay) { // we still need to send the block changes of that chunk
BukkitAdapter_1_16_5.sendChunk(world, x, z, -1, false);
}
world.getChunkProvider().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
}
}
@Override
public void clear() {
}
@Override
public void removeLighting() {
this.delegate.removeLighting();
}
@Override
public void fixBlockLighting() {
fixLightingSafe(true);
}
@Override
public void fixSkyLighting() {
fixLightingSafe(true);
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public ReentrantLock getLock() {
return this.lock;
}
@Override
public boolean isFinished() {
return false;
}
@Override
public void close() throws Exception {
fixLightingSafe(true);
}
}

View File

@ -19,6 +19,9 @@
package com.sk89q.worldedit.bukkit;
import com.boydti.fawe.beta.implementation.lighting.RelighterFactory;
import com.boydti.fawe.bukkit.NMSRelighterFactory;
import com.boydti.fawe.bukkit.adapter.mc1_16_5.TuinityRelighterFactory_1_16_5;
import com.google.common.collect.Sets;
import com.sk89q.bukkit.util.CommandInfo;
import com.sk89q.bukkit.util.CommandRegistration;
@ -32,15 +35,18 @@ import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.MultiUserPlatform;
import com.sk89q.worldedit.extension.platform.Preference;
import com.sk89q.worldedit.extension.platform.Watchdog;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.concurrency.LazyReference;
import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.registry.Registries;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.enginehub.piston.CommandManager;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
@ -56,10 +62,13 @@ import static com.sk89q.worldedit.util.formatting.WorldEditText.reduceToText;
public class BukkitServerInterface extends AbstractPlatform implements MultiUserPlatform {
private static final Logger LOGGER = LogManagerCompat.getLogger();
public final Server server;
public final WorldEditPlugin plugin;
private final CommandRegistration dynamicCommands;
private final LazyReference<Watchdog> watchdog;
private final RelighterFactory religherFactory;
private boolean hookingEvents;
public BukkitServerInterface(WorldEditPlugin plugin, Server server) {
@ -74,6 +83,16 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
}
return null;
});
RelighterFactory tempFactory;
try {
Class.forName("com.tuinity.tuinity.config.TuinityConfig");
tempFactory = new TuinityRelighterFactory_1_16_5();
LOGGER.info("Using Tuinity internals for relighting");
} catch (ClassNotFoundException e) {
tempFactory = new NMSRelighterFactory();
LOGGER.info("Using FAWE for relighting");
}
this.religherFactory = tempFactory;
}
CommandRegistration getDynamicCommands() {
@ -244,6 +263,11 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
return SUPPORTED_SIDE_EFFECTS;
}
@Override
public @NotNull RelighterFactory getRelighterFactory() {
return this.religherFactory;
}
public void unregisterCommands() {
dynamicCommands.unregisterCommands();
}