Telesphoreo 2023-11-13 19:15:11 -06:00
commit 6485772826
No known key found for this signature in database
GPG Key ID: 9D1991811E093C02
32 changed files with 370 additions and 819 deletions

View File

@ -34,7 +34,7 @@ logger.lifecycle("""
******************************************* *******************************************
""") """)
var rootVersion by extra("2.8.2") var rootVersion by extra("2.8.3")
var snapshot by extra("SNAPSHOT") var snapshot by extra("SNAPSHOT")
var revision: String by extra("") var revision: String by extra("")
var buildNumber by extra("") var buildNumber by extra("")

View File

@ -22,7 +22,7 @@ val properties = Properties().also { props ->
dependencies { dependencies {
implementation(gradleApi()) implementation(gradleApi())
implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0") implementation("org.ajoberstar.grgit:grgit-gradle:5.2.1")
implementation("com.github.johnrengelman:shadow:8.1.1") implementation("com.github.johnrengelman:shadow:8.1.1")
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5") implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5")
} }

View File

@ -14,8 +14,8 @@ mapmanager = "1.8.0-SNAPSHOT"
griefprevention = "16.18.1" griefprevention = "16.18.1"
griefdefender = "2.1.0-SNAPSHOT" griefdefender = "2.1.0-SNAPSHOT"
residence = "4.5._13.1" residence = "4.5._13.1"
towny = "0.99.6.0" towny = "0.100.0.1"
plotsquared = "7.0.0" plotsquared = "7.1.0"
# Third party # Third party
bstats = "3.0.2" bstats = "3.0.2"
@ -23,7 +23,7 @@ sparsebitset = "1.3"
parallelgzip = "1.0.5" parallelgzip = "1.0.5"
adventure = "4.14.0" adventure = "4.14.0"
adventure-bukkit = "4.3.1" adventure-bukkit = "4.3.1"
checkerqual = "3.39.0" checkerqual = "3.40.0"
truezip = "6.8.4" truezip = "6.8.4"
auto-value = "1.10.4" auto-value = "1.10.4"
findbugs = "3.0.2" findbugs = "3.0.2"
@ -35,7 +35,7 @@ jlibnoise = "1.0.0"
jchronic = "0.2.4a" jchronic = "0.2.4a"
lz4-java = "1.8.0" lz4-java = "1.8.0"
lz4-stream = "1.0.0" lz4-stream = "1.0.0"
commons-cli = "1.5.0" commons-cli = "1.6.0"
paperlib = "1.0.8" paperlib = "1.0.8"
paster = "1.1.5" paster = "1.1.5"
vault = "1.7.1" vault = "1.7.1"

View File

@ -1,17 +1,8 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.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.MCUtil; import net.minecraft.server.MCUtil;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine; import net.minecraft.server.level.ThreadedLevelLightEngine;
@ -19,27 +10,18 @@ import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit; import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.IntConsumer; import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter implements Relighter { public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
public static final MethodHandle RELIGHT;
private static final Logger LOGGER = LogManagerCompat.getLogger();
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 MethodHandle RELIGHT;
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0); private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT); private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
@ -58,22 +40,36 @@ public class PaperweightStarlightRelighter implements Relighter {
IntConsumer.class IntConsumer.class
) )
); );
tmp = MethodHandles.dropReturn(tmp);
} catch (NoSuchMethodException | IllegalAccessException e) { } catch (NoSuchMethodException | IllegalAccessException e) {
LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e); LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e);
} }
RELIGHT = tmp; RELIGHT = tmp;
} }
private final ServerLevel serverLevel; public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
private final ReentrantLock lock = new ReentrantLock(); super(serverLevel, queue);
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>(); }
private final ReentrantLock areaLock = new ReentrantLock();
private final NMSRelighter delegate;
@SuppressWarnings("rawtypes") @Override
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) { protected ChunkPos createChunkPos(final long chunkKey) {
this.serverLevel = serverLevel; return new ChunkPos(chunkKey);
this.delegate = new NMSRelighter(queue); }
@Override
protected long asLong(final int chunkX, final int chunkZ) {
return ChunkPos.asLong(chunkX, chunkZ);
}
@Override
protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
FAWE_TICKET,
chunkPos,
LIGHT_LEVEL,
Unit.INSTANCE
));
} }
public static boolean isUsable() { public static boolean isUsable() {
@ -81,95 +77,13 @@ public class PaperweightStarlightRelighter implements Relighter {
} }
@Override @Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) { protected void invokeRelight(
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(ChunkPos.asLong(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 ChunkPos
Set<ChunkPos> coords = new HashSet<>();
LongIterator iterator = chunks.iterator();
while (iterator.hasNext()) {
coords.add(new ChunkPos(iterator.nextLong()));
}
TaskManager.taskManager().task(() -> {
// trigger chunk load and apply ticket on main thread
List<CompletableFuture<?>> futures = new ArrayList<>();
for (ChunkPos pos : coords) {
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
.thenAccept(c -> serverLevel.getChunkSource().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 {} chunks instead of {}", i, coords.size());
}
// post process chunks on main thread
TaskManager.taskManager().task(() -> postProcessChunks(coords));
// call callback on our own threads
TaskManager.taskManager().async(andThen);
}
)
);
});
}
private void invokeRelight(
Set<ChunkPos> coords, Set<ChunkPos> coords,
Consumer<ChunkPos> chunkCallback, Consumer<ChunkPos> chunkCallback,
IntConsumer processCallback IntConsumer processCallback
) { ) {
try { try {
int unused = (int) RELIGHT.invokeExact( RELIGHT.invokeExact(
serverLevel.getChunkSource().getLightEngine(), serverLevel.getChunkSource().getLightEngine(),
coords, coords,
chunkCallback, // callback per chunk chunkCallback, // callback per chunk
@ -184,7 +98,7 @@ public class PaperweightStarlightRelighter implements Relighter {
* Allow the server to unload the chunks again. * Allow the server to unload the chunks again.
* Also, if chunk packets are sent delayed, we need to do that here * Also, if chunk packets are sent delayed, we need to do that here
*/ */
private void postProcessChunks(Set<ChunkPos> coords) { protected void postProcessChunks(Set<ChunkPos> coords) {
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING; boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
for (ChunkPos pos : coords) { for (ChunkPos pos : coords) {
int x = pos.x; int x = pos.x;
@ -196,44 +110,4 @@ public class PaperweightStarlightRelighter implements Relighter {
} }
} }
@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

@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
public class PaperweightStarlightRelighterFactory implements RelighterFactory { public class PaperweightStarlightRelighterFactory implements RelighterFactory {
@Override @Override
public @Nonnull public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
@SuppressWarnings("rawtypes")
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
org.bukkit.World w = Bukkit.getWorld(world.getName()); org.bukkit.World w = Bukkit.getWorld(world.getName());
if (w == null) { if (w == null) {
return NullRelighter.INSTANCE; return NullRelighter.INSTANCE;

View File

@ -1,140 +1,51 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.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.MCUtil; import net.minecraft.server.MCUtil;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit; import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.IntConsumer; import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter implements Relighter { public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
private static final Logger LOGGER = LogManagerCompat.getLogger();
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.create("fawe_ticket", (a, b) -> 0); private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT); private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
private final ServerLevel serverLevel; super(serverLevel, queue);
private final ReentrantLock lock = new ReentrantLock();
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
private final ReentrantLock areaLock = new ReentrantLock();
private final NMSRelighter delegate;
@SuppressWarnings("rawtypes")
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
this.serverLevel = serverLevel;
this.delegate = new NMSRelighter(queue);
} }
@Override @Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) { protected ChunkPos createChunkPos(final long chunkKey) {
areaLock.lock(); return new ChunkPos(chunkKey);
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(ChunkPos.asLong(cx, cz));
} finally {
areaLock.unlock();
}
return true;
} }
@Override @Override
public void addLightUpdate(int x, int y, int z) { protected long asLong(final int chunkX, final int chunkZ) {
delegate.addLightUpdate(x, y, z); return ChunkPos.asLong(chunkX, chunkZ);
} }
/*
* 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 @Override
public void fixLightingSafe(boolean sky) { protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
this.areaLock.lock(); return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
try { .thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
if (regions.isEmpty()) { FAWE_TICKET,
return; chunkPos,
} LIGHT_LEVEL,
LongSet first = regions.removeFirst(); Unit.INSTANCE
fixLighting(first, () -> fixLightingSafe(true)); ));
} finally {
this.areaLock.unlock();
}
} }
/* protected void invokeRelight(
* 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 ChunkPos
Set<ChunkPos> coords = new HashSet<>();
LongIterator iterator = chunks.iterator();
while (iterator.hasNext()) {
coords.add(new ChunkPos(iterator.nextLong()));
}
TaskManager.taskManager().task(() -> {
// trigger chunk load and apply ticket on main thread
List<CompletableFuture<?>> futures = new ArrayList<>();
for (ChunkPos pos : coords) {
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
.thenAccept(c -> serverLevel.getChunkSource().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 {} chunks instead of {}", i, coords.size());
}
// post process chunks on main thread
TaskManager.taskManager().task(() -> postProcessChunks(coords));
// call callback on our own threads
TaskManager.taskManager().async(andThen);
}
)
);
});
}
private void invokeRelight(
Set<ChunkPos> coords, Set<ChunkPos> coords,
Consumer<ChunkPos> chunkCallback, Consumer<ChunkPos> chunkCallback,
IntConsumer processCallback IntConsumer processCallback
@ -150,7 +61,7 @@ public class PaperweightStarlightRelighter implements Relighter {
* Allow the server to unload the chunks again. * Allow the server to unload the chunks again.
* Also, if chunk packets are sent delayed, we need to do that here * Also, if chunk packets are sent delayed, we need to do that here
*/ */
private void postProcessChunks(Set<ChunkPos> coords) { protected void postProcessChunks(Set<ChunkPos> coords) {
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING; boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
for (ChunkPos pos : coords) { for (ChunkPos pos : coords) {
int x = pos.x; int x = pos.x;
@ -162,44 +73,4 @@ public class PaperweightStarlightRelighter implements Relighter {
} }
} }
@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

@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
public class PaperweightStarlightRelighterFactory implements RelighterFactory { public class PaperweightStarlightRelighterFactory implements RelighterFactory {
@Override @Override
public @Nonnull public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
@SuppressWarnings("rawtypes")
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
org.bukkit.World w = Bukkit.getWorld(world.getName()); org.bukkit.World w = Bukkit.getWorld(world.getName());
if (w == null) { if (w == null) {
return NullRelighter.INSTANCE; return NullRelighter.INSTANCE;

View File

@ -1,140 +1,51 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.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.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit; import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.IntConsumer; import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter implements Relighter { public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
private static final Logger LOGGER = LogManagerCompat.getLogger();
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.create("fawe_ticket", (a, b) -> 0); private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT); private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
private final ServerLevel serverLevel; super(serverLevel, queue);
private final ReentrantLock lock = new ReentrantLock();
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
private final ReentrantLock areaLock = new ReentrantLock();
private final NMSRelighter delegate;
@SuppressWarnings("rawtypes")
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
this.serverLevel = serverLevel;
this.delegate = new NMSRelighter(queue);
} }
@Override @Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) { protected ChunkPos createChunkPos(final long chunkKey) {
areaLock.lock(); return new ChunkPos(chunkKey);
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(ChunkPos.asLong(cx, cz));
} finally {
areaLock.unlock();
}
return true;
} }
@Override @Override
public void addLightUpdate(int x, int y, int z) { protected long asLong(final int chunkX, final int chunkZ) {
delegate.addLightUpdate(x, y, z); return ChunkPos.asLong(chunkX, chunkZ);
} }
/*
* 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 @Override
public void fixLightingSafe(boolean sky) { protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
this.areaLock.lock(); return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
try { .thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
if (regions.isEmpty()) { FAWE_TICKET,
return; chunkPos,
} LIGHT_LEVEL,
LongSet first = regions.removeFirst(); Unit.INSTANCE
fixLighting(first, () -> fixLightingSafe(true)); ));
} finally {
this.areaLock.unlock();
}
} }
/* protected void invokeRelight(
* 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 ChunkPos
Set<ChunkPos> coords = new HashSet<>();
LongIterator iterator = chunks.iterator();
while (iterator.hasNext()) {
coords.add(new ChunkPos(iterator.nextLong()));
}
TaskManager.taskManager().task(() -> {
// trigger chunk load and apply ticket on main thread
List<CompletableFuture<?>> futures = new ArrayList<>();
for (ChunkPos pos : coords) {
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
.thenAccept(c -> serverLevel.getChunkSource().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 {} chunks instead of {}", i, coords.size());
}
// post process chunks on main thread
TaskManager.taskManager().task(() -> postProcessChunks(coords));
// call callback on our own threads
TaskManager.taskManager().async(andThen);
}
)
);
});
}
private void invokeRelight(
Set<ChunkPos> coords, Set<ChunkPos> coords,
Consumer<ChunkPos> chunkCallback, Consumer<ChunkPos> chunkCallback,
IntConsumer processCallback IntConsumer processCallback
@ -150,7 +61,7 @@ public class PaperweightStarlightRelighter implements Relighter {
* Allow the server to unload the chunks again. * Allow the server to unload the chunks again.
* Also, if chunk packets are sent delayed, we need to do that here * Also, if chunk packets are sent delayed, we need to do that here
*/ */
private void postProcessChunks(Set<ChunkPos> coords) { protected void postProcessChunks(Set<ChunkPos> coords) {
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING; boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
for (ChunkPos pos : coords) { for (ChunkPos pos : coords) {
int x = pos.x; int x = pos.x;
@ -162,44 +73,4 @@ public class PaperweightStarlightRelighter implements Relighter {
} }
} }
@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

@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
public class PaperweightStarlightRelighterFactory implements RelighterFactory { public class PaperweightStarlightRelighterFactory implements RelighterFactory {
@Override @Override
public @Nonnull public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
@SuppressWarnings("rawtypes")
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
org.bukkit.World w = Bukkit.getWorld(world.getName()); org.bukkit.World w = Bukkit.getWorld(world.getName());
if (w == null) { if (w == null) {
return NullRelighter.INSTANCE; return NullRelighter.INSTANCE;

View File

@ -1,140 +1,51 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.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.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit; import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.IntConsumer; import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter implements Relighter { public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
private static final Logger LOGGER = LogManagerCompat.getLogger();
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.create("fawe_ticket", (a, b) -> 0); private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT); private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
private final ServerLevel serverLevel; super(serverLevel, queue);
private final ReentrantLock lock = new ReentrantLock();
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
private final ReentrantLock areaLock = new ReentrantLock();
private final NMSRelighter delegate;
@SuppressWarnings("rawtypes")
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
this.serverLevel = serverLevel;
this.delegate = new NMSRelighter(queue);
} }
@Override @Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) { protected ChunkPos createChunkPos(final long chunkKey) {
areaLock.lock(); return new ChunkPos(chunkKey);
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(ChunkPos.asLong(cx, cz));
} finally {
areaLock.unlock();
}
return true;
} }
@Override @Override
public void addLightUpdate(int x, int y, int z) { protected long asLong(final int chunkX, final int chunkZ) {
delegate.addLightUpdate(x, y, z); return ChunkPos.asLong(chunkX, chunkZ);
} }
/*
* 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 @Override
public void fixLightingSafe(boolean sky) { protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
this.areaLock.lock(); return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
try { .thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
if (regions.isEmpty()) { FAWE_TICKET,
return; chunkPos,
} LIGHT_LEVEL,
LongSet first = regions.removeFirst(); Unit.INSTANCE
fixLighting(first, () -> fixLightingSafe(true)); ));
} finally {
this.areaLock.unlock();
}
} }
/* protected void invokeRelight(
* 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 ChunkPos
Set<ChunkPos> coords = new HashSet<>();
LongIterator iterator = chunks.iterator();
while (iterator.hasNext()) {
coords.add(new ChunkPos(iterator.nextLong()));
}
TaskManager.taskManager().task(() -> {
// trigger chunk load and apply ticket on main thread
List<CompletableFuture<?>> futures = new ArrayList<>();
for (ChunkPos pos : coords) {
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
.thenAccept(c -> serverLevel.getChunkSource().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 {} chunks instead of {}", i, coords.size());
}
// post process chunks on main thread
TaskManager.taskManager().task(() -> postProcessChunks(coords));
// call callback on our own threads
TaskManager.taskManager().async(andThen);
}
)
);
});
}
private void invokeRelight(
Set<ChunkPos> coords, Set<ChunkPos> coords,
Consumer<ChunkPos> chunkCallback, Consumer<ChunkPos> chunkCallback,
IntConsumer processCallback IntConsumer processCallback
@ -150,7 +61,7 @@ public class PaperweightStarlightRelighter implements Relighter {
* Allow the server to unload the chunks again. * Allow the server to unload the chunks again.
* Also, if chunk packets are sent delayed, we need to do that here * Also, if chunk packets are sent delayed, we need to do that here
*/ */
private void postProcessChunks(Set<ChunkPos> coords) { protected void postProcessChunks(Set<ChunkPos> coords) {
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING; boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
for (ChunkPos pos : coords) { for (ChunkPos pos : coords) {
int x = pos.x; int x = pos.x;
@ -162,44 +73,4 @@ public class PaperweightStarlightRelighter implements Relighter {
} }
} }
@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

@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
public class PaperweightStarlightRelighterFactory implements RelighterFactory { public class PaperweightStarlightRelighterFactory implements RelighterFactory {
@Override @Override
public @Nonnull public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
@SuppressWarnings("rawtypes")
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
org.bukkit.World w = Bukkit.getWorld(world.getName()); org.bukkit.World w = Bukkit.getWorld(world.getName());
if (w == null) { if (w == null) {
return NullRelighter.INSTANCE; return NullRelighter.INSTANCE;

View File

@ -12,6 +12,6 @@ repositories {
dependencies { dependencies {
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.2-R0.1-20231008.101509-26") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.2-R0.1-20231029.153906-63")
compileOnly(libs.paperlib) compileOnly(libs.paperlib)
} }

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2; package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
@ -28,8 +28,6 @@ import com.google.common.util.concurrent.Futures;
import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Lifecycle; import com.mojang.serialization.Lifecycle;
import com.sk89q.jnbt.NBTConstants; import com.sk89q.jnbt.NBTConstants;
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2.PaperweightDataConverters;
import com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2.PaperweightFakePlayer;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
@ -358,8 +356,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
@Override @Override
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) { public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
return new com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2.PaperweightWorldNativeAccess(this, return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle()));
new WeakReference<>(((CraftWorld) world).getHandle()));
} }
private static net.minecraft.core.Direction adapt(Direction face) { private static net.minecraft.core.Direction adapt(Direction face) {

View File

@ -168,7 +168,7 @@ public class PaperweightDataConverters extends DataFixerBuilder implements com.s
.getValue().getAsString(); .getValue().getAsString();
} }
private final com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2.PaperweightAdapter adapter; private final PaperweightAdapter adapter;
private static final NbtOps OPS_NBT = NbtOps.INSTANCE; private static final NbtOps OPS_NBT = NbtOps.INSTANCE;
private static final int LEGACY_VERSION = 1343; private static final int LEGACY_VERSION = 1343;
@ -204,8 +204,7 @@ public class PaperweightDataConverters extends DataFixerBuilder implements com.s
} }
} }
public PaperweightDataConverters(int dataVersion, public PaperweightDataConverters(int dataVersion, PaperweightAdapter adapter) {
com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2.PaperweightAdapter adapter) {
super(dataVersion); super(dataVersion);
DATA_VERSION = dataVersion; DATA_VERSION = dataVersion;
INSTANCE = this; INSTANCE = this;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2; package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2; package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
@ -43,11 +43,11 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess<LevelChun
private static final int UPDATE = 1; private static final int UPDATE = 1;
private static final int NOTIFY = 2; private static final int NOTIFY = 2;
private final com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2.PaperweightAdapter adapter; private final PaperweightAdapter adapter;
private final WeakReference<ServerLevel> world; private final WeakReference<ServerLevel> world;
private SideEffectSet sideEffectSet; private SideEffectSet sideEffectSet;
public PaperweightWorldNativeAccess(com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2.PaperweightAdapter adapter, WeakReference<ServerLevel> world) { public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference<ServerLevel> world) {
this.adapter = adapter; this.adapter = adapter;
this.world = world; this.world = world;
} }

View File

@ -20,7 +20,6 @@ import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.v1_20_R2.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
@ -124,7 +123,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
} }
} }
private final PaperweightAdapter parent; private final com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2.PaperweightAdapter parent;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Code that may break between versions of Minecraft // Code that may break between versions of Minecraft
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -135,7 +134,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
private Map<String, List<Property<?>>> allBlockProperties = null; private Map<String, List<Property<?>>> allBlockProperties = null;
public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException { public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
this.parent = new PaperweightAdapter(); this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2.PaperweightAdapter();
} }
@Nullable @Nullable

View File

@ -1,140 +1,51 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.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.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit; import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.IntConsumer; import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter implements Relighter { public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLevel, ChunkPos> {
private static final Logger LOGGER = LogManagerCompat.getLogger();
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.create("fawe_ticket", (a, b) -> 0); private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT); private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<?> queue) {
private final ServerLevel serverLevel; super(serverLevel, queue);
private final ReentrantLock lock = new ReentrantLock();
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
private final ReentrantLock areaLock = new ReentrantLock();
private final NMSRelighter delegate;
@SuppressWarnings("rawtypes")
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
this.serverLevel = serverLevel;
this.delegate = new NMSRelighter(queue);
} }
@Override @Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) { protected ChunkPos createChunkPos(final long chunkKey) {
areaLock.lock(); return new ChunkPos(chunkKey);
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(ChunkPos.asLong(cx, cz));
} finally {
areaLock.unlock();
}
return true;
} }
@Override @Override
public void addLightUpdate(int x, int y, int z) { protected long asLong(final int chunkX, final int chunkZ) {
delegate.addLightUpdate(x, y, z); return ChunkPos.asLong(chunkX, chunkZ);
} }
/*
* 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 @Override
public void fixLightingSafe(boolean sky) { protected CompletableFuture<?> chunkLoadFuture(final ChunkPos chunkPos) {
this.areaLock.lock(); return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z)
try { .thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
if (regions.isEmpty()) { FAWE_TICKET,
return; chunkPos,
} LIGHT_LEVEL,
LongSet first = regions.removeFirst(); Unit.INSTANCE
fixLighting(first, () -> fixLightingSafe(true)); ));
} finally {
this.areaLock.unlock();
}
} }
/* protected void invokeRelight(
* 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 ChunkPos
Set<ChunkPos> coords = new HashSet<>();
LongIterator iterator = chunks.iterator();
while (iterator.hasNext()) {
coords.add(new ChunkPos(iterator.nextLong()));
}
TaskManager.taskManager().task(() -> {
// trigger chunk load and apply ticket on main thread
List<CompletableFuture<?>> futures = new ArrayList<>();
for (ChunkPos pos : coords) {
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
.thenAccept(c -> serverLevel.getChunkSource().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 {} chunks instead of {}", i, coords.size());
}
// post process chunks on main thread
TaskManager.taskManager().task(() -> postProcessChunks(coords));
// call callback on our own threads
TaskManager.taskManager().async(andThen);
}
)
);
});
}
private void invokeRelight(
Set<ChunkPos> coords, Set<ChunkPos> coords,
Consumer<ChunkPos> chunkCallback, Consumer<ChunkPos> chunkCallback,
IntConsumer processCallback IntConsumer processCallback
@ -150,7 +61,7 @@ public class PaperweightStarlightRelighter implements Relighter {
* Allow the server to unload the chunks again. * Allow the server to unload the chunks again.
* Also, if chunk packets are sent delayed, we need to do that here * Also, if chunk packets are sent delayed, we need to do that here
*/ */
private void postProcessChunks(Set<ChunkPos> coords) { protected void postProcessChunks(Set<ChunkPos> coords) {
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING; boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
for (ChunkPos pos : coords) { for (ChunkPos pos : coords) {
int x = pos.x; int x = pos.x;
@ -162,44 +73,4 @@ public class PaperweightStarlightRelighter implements Relighter {
} }
} }
@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

@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -15,9 +14,7 @@ import javax.annotation.Nonnull;
public class PaperweightStarlightRelighterFactory implements RelighterFactory { public class PaperweightStarlightRelighterFactory implements RelighterFactory {
@Override @Override
public @Nonnull public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
@SuppressWarnings("rawtypes")
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
org.bukkit.World w = Bukkit.getWorld(world.getName()); org.bukkit.World w = Bukkit.getWorld(world.getName());
if (w == null) { if (w == null) {
return NullRelighter.INSTANCE; return NullRelighter.INSTANCE;

View File

@ -5,7 +5,6 @@ import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
@ -14,8 +13,7 @@ import javax.annotation.Nonnull;
public class NMSRelighterFactory implements RelighterFactory { public class NMSRelighterFactory implements RelighterFactory {
@Override @Override
public @Nonnull public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue) {
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
return new NMSRelighter( return new NMSRelighter(
queue, queue,
relightMode != null ? relightMode : RelightMode.valueOf(Settings.settings().LIGHTING.MODE) relightMode != null ? relightMode : RelightMode.valueOf(Settings.settings().LIGHTING.MODE)

View File

@ -0,0 +1,196 @@
package com.fastasyncworldedit.bukkit.adapter;
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.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 org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
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;
/**
* A base class for version-specific implementations of the starlight relighting mechanism
*
* @param <SERVER_LEVEL> the version-specific ServerLevel type
* @param <CHUNK_POS> the version-specific ChunkPos type
* @since 2.8.2
*/
public abstract class StarlightRelighter<SERVER_LEVEL, CHUNK_POS> implements Relighter {
protected static final Logger LOGGER = LogManagerCompat.getLogger();
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
private static final int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
private final ReentrantLock lock = new ReentrantLock();
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
private final ReentrantLock areaLock = new ReentrantLock();
private final NMSRelighter delegate;
protected final SERVER_LEVEL serverLevel;
protected StarlightRelighter(SERVER_LEVEL serverLevel, IQueueExtent<?> queue) {
this.serverLevel = serverLevel;
this.delegate = new NMSRelighter(queue);
}
protected Set<CHUNK_POS> convertChunkKeysToChunkPos(LongSet chunks) {
// convert from long keys to ChunkPos
Set<CHUNK_POS> coords = new HashSet<>();
LongIterator iterator = chunks.iterator();
while (iterator.hasNext()) {
coords.add(createChunkPos(iterator.nextLong()));
}
return coords;
}
protected abstract CHUNK_POS createChunkPos(long chunkKey);
protected abstract long asLong(int chunkX, int chunkZ);
protected abstract CompletableFuture<?> chunkLoadFuture(CHUNK_POS pos);
protected List<CompletableFuture<?>> chunkLoadFutures(Set<CHUNK_POS> coords) {
List<CompletableFuture<?>> futures = new ArrayList<>();
for (final CHUNK_POS coord : coords) {
futures.add(chunkLoadFuture(coord));
}
return futures;
}
@NotNull
protected IntConsumer postProcessCallback(Runnable andThen, Set<CHUNK_POS> coords) {
return i -> {
if (i != coords.size()) {
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
}
// post process chunks on main thread
TaskManager.taskManager().task(() -> postProcessChunks(coords));
// call callback on our own threads
TaskManager.taskManager().async(andThen);
};
}
protected abstract void invokeRelight(
Set<CHUNK_POS> coords,
Consumer<CHUNK_POS> chunkCallback,
IntConsumer processCallback
);
protected abstract void postProcessChunks(Set<CHUNK_POS> coords);
/*
* 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).
*/
protected void fixLighting(LongSet chunks, Runnable andThen) {
Set<CHUNK_POS> coords = convertChunkKeysToChunkPos(chunks);
TaskManager.taskManager().task(() -> {
// trigger chunk load and apply ticket on main thread
List<CompletableFuture<?>> futures = chunkLoadFutures(coords);
// 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
postProcessCallback(andThen, coords)
)
);
});
}
@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(asLong(cx, cz));
} finally {
areaLock.unlock();
}
return true;
}
/*
* 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();
}
}
@Override
public void addLightUpdate(int x, int y, int z) {
this.delegate.addLightUpdate(x, y, z);
}
@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

@ -60,16 +60,17 @@ import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
public enum FaweCache implements Trimable { public enum FaweCache implements Trimable {
/**
* @deprecated Use {@link #INSTANCE} to get an instance.
*/
@Deprecated(forRemoval = true, since = "2.0.0")
IMP,
/** /**
* @since 2.0.0 * @since 2.0.0
*/ */
INSTANCE; INSTANCE;
/**
* @deprecated Use {@link #INSTANCE} to get an instance.
*/
@Deprecated(forRemoval = true, since = "2.0.0")
public static final FaweCache IMP = INSTANCE;
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
public final int BLOCKS_PER_LAYER = 4096; public final int BLOCKS_PER_LAYER = 4096;

View File

@ -19,14 +19,14 @@ import java.util.stream.Stream;
public class Settings extends Config { public class Settings extends Config {
@Ignore
static Settings INSTANCE = new Settings();
/** /**
* @deprecated Use {@link #settings()} instead to get an instance. * @deprecated Use {@link #settings()} instead to get an instance.
*/ */
@Ignore @Ignore
@Deprecated(forRemoval = true, since = "2.0.0") @Deprecated(forRemoval = true, since = "2.0.0")
public static final Settings IMP = new Settings(); public static final Settings IMP = INSTANCE;
@Ignore
static Settings INSTANCE = new Settings();
@Ignore @Ignore
public boolean PROTOCOL_SUPPORT_FIX = false; public boolean PROTOCOL_SUPPORT_FIX = false;
@Comment("These first 6 aren't configurable") // This is a comment @Comment("These first 6 aren't configurable") // This is a comment

View File

@ -13,7 +13,7 @@ public class DBHandler {
* @deprecated Use {@link #dbHandler()} instead. * @deprecated Use {@link #dbHandler()} instead.
*/ */
@Deprecated(forRemoval = true, since = "2.0.0") @Deprecated(forRemoval = true, since = "2.0.0")
public static final DBHandler IMP = new DBHandler(); public static final DBHandler IMP = dbHandler();
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static DBHandler INSTANCE; private static DBHandler INSTANCE;
private final Map<World, RollbackDatabase> databases = new ConcurrentHashMap<>(8, 0.9f, 1); private final Map<World, RollbackDatabase> databases = new ConcurrentHashMap<>(8, 0.9f, 1);

View File

@ -4,7 +4,6 @@ import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.math.BlockVectorSet; import com.fastasyncworldedit.core.math.BlockVectorSet;
import com.fastasyncworldedit.core.math.MutableBlockVector3; import com.fastasyncworldedit.core.math.MutableBlockVector3;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkHolder; import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkHolder;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
@ -34,7 +33,6 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@SuppressWarnings("rawtypes")
public class NMSRelighter implements Relighter { public class NMSRelighter implements Relighter {
private static final int DISPATCH_SIZE = 64; private static final int DISPATCH_SIZE = 64;
@ -51,7 +49,7 @@ public class NMSRelighter implements Relighter {
} }
public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0); public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0);
private final IQueueExtent<IQueueChunk> queue; private final IQueueExtent<?> queue;
private final Map<Long, RelightSkyEntry> skyToRelight; private final Map<Long, RelightSkyEntry> skyToRelight;
private final Object present = new Object(); private final Object present = new Object();
private final Map<Long, Integer> chunksToSend; private final Map<Long, Integer> chunksToSend;
@ -66,11 +64,11 @@ public class NMSRelighter implements Relighter {
private final AtomicBoolean finished = new AtomicBoolean(false); private final AtomicBoolean finished = new AtomicBoolean(false);
private boolean removeFirst; private boolean removeFirst;
public NMSRelighter(IQueueExtent<IQueueChunk> queue) { public NMSRelighter(IQueueExtent<?> queue) {
this(queue, null); this(queue, null);
} }
public NMSRelighter(IQueueExtent<IQueueChunk> queue, RelightMode relightMode) { public NMSRelighter(IQueueExtent<?> queue, RelightMode relightMode) {
this.queue = queue; this.queue = queue;
this.skyToRelight = new Long2ObjectOpenHashMap<>(12); this.skyToRelight = new Long2ObjectOpenHashMap<>(12);
this.lightQueue = new Long2ObjectOpenHashMap<>(12); this.lightQueue = new Long2ObjectOpenHashMap<>(12);

View File

@ -1,6 +1,5 @@
package com.fastasyncworldedit.core.extent.processor.lighting; package com.fastasyncworldedit.core.extent.processor.lighting;
import com.fastasyncworldedit.core.queue.IQueueChunk;
import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.IQueueExtent;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
@ -25,6 +24,6 @@ public interface RelighterFactory {
* @return a new Relighter instance with the specified settings. * @return a new Relighter instance with the specified settings.
*/ */
@Nonnull @Nonnull
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue); Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<?> queue);
} }

View File

@ -64,14 +64,14 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput {
* Lock the {@link IChunkGet#call(IChunkSet, Runnable)} method to the current thread using a reentrant lock. Also locks * Lock the {@link IChunkGet#call(IChunkSet, Runnable)} method to the current thread using a reentrant lock. Also locks
* related methods e.g. {@link IChunkGet#setCreateCopy(boolean)} * related methods e.g. {@link IChunkGet#setCreateCopy(boolean)}
* *
* @since TODO * @since 2.8.2
*/ */
default void lockCall() {} default void lockCall() {}
/** /**
* Unlock {@link IChunkGet#call(IChunkSet, Runnable)} (and other related methods) to executions from other threads * Unlock {@link IChunkGet#call(IChunkSet, Runnable)} (and other related methods) to executions from other threads
* *
* @since TODO * @since 2.8.2
*/ */
default void unlockCall() {} default void unlockCall() {}

View File

@ -135,9 +135,6 @@ public interface IQueueExtent<T extends IChunk> extends Flushable, Trimable, ICh
return block; return block;
} }
T chunk = this.getOrCreateChunk(chunkX, chunkZ); T chunk = this.getOrCreateChunk(chunkX, chunkZ);
// Initialize
chunk.init(this, chunkX, chunkZ);
chunk.setFastMode(isFastMode());
T newChunk = filter.applyChunk(chunk, region); T newChunk = filter.applyChunk(chunk, region);
if (newChunk != null) { if (newChunk != null) {

View File

@ -32,7 +32,7 @@ public class WEManager {
* @deprecated Use {@link #weManager()} instead. * @deprecated Use {@link #weManager()} instead.
*/ */
@Deprecated(forRemoval = true, since = "2.0.0") @Deprecated(forRemoval = true, since = "2.0.0")
public static WEManager IMP = new WEManager(); public static WEManager IMP = weManager();
private final ArrayDeque<FaweMaskManager> managers = new ArrayDeque<>(); private final ArrayDeque<FaweMaskManager> managers = new ArrayDeque<>();
/** /**

View File

@ -33,6 +33,7 @@ import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.util.BrushCache; import com.fastasyncworldedit.core.util.BrushCache;
import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.StringMan; import com.fastasyncworldedit.core.util.StringMan;
import com.fastasyncworldedit.core.util.TaskManager;
import com.fastasyncworldedit.core.util.TextureHolder; import com.fastasyncworldedit.core.util.TextureHolder;
import com.fastasyncworldedit.core.util.TextureUtil; import com.fastasyncworldedit.core.util.TextureUtil;
import com.fastasyncworldedit.core.wrappers.WorldWrapper; import com.fastasyncworldedit.core.wrappers.WorldWrapper;
@ -99,7 +100,6 @@ import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -143,7 +143,7 @@ public class LocalSession implements TextureHolder {
} }
}); });
private transient volatile Integer historyNegativeIndex; private transient volatile Integer historyNegativeIndex;
private transient final Lock historyWriteLock = new ReentrantLock(true); private transient final ReentrantLock historyWriteLock = new ReentrantLock(true);
private final transient Int2ObjectOpenHashMap<Tool> tools = new Int2ObjectOpenHashMap<>(0); private final transient Int2ObjectOpenHashMap<Tool> tools = new Int2ObjectOpenHashMap<>(0);
private transient Mask sourceMask; private transient Mask sourceMask;
private transient TextureUtil texture; private transient TextureUtil texture;
@ -405,6 +405,23 @@ public class LocalSession implements TextureHolder {
*/ */
public void clearHistory() { public void clearHistory() {
//FAWE start //FAWE start
boolean mainThread = Fawe.isMainThread();
if (mainThread && !historyWriteLock.tryLock()) {
// Do not make main thread wait if we cannot immediately clear history (on player logout usually)
TaskManager.taskManager().async(this::clearHistoryTask);
return;
}
try {
clearHistoryTask();
} finally {
// only if we are on the main thread, we ever called tryLock -> need to unlock again
if (mainThread) {
historyWriteLock.unlock();
}
}
}
private void clearHistoryTask() {
historyWriteLock.lock(); historyWriteLock.lock();
try { try {
// Ensure that changesets are properly removed // Ensure that changesets are properly removed
@ -420,8 +437,8 @@ public class LocalSession implements TextureHolder {
save(); save();
historySize = 0; historySize = 0;
currentWorld = null; currentWorld = null;
//FAWE end
} }
//FAWE end
/** /**
* Remember an edit session for the undo history. If the history maximum * Remember an edit session for the undo history. If the history maximum

View File

@ -29,7 +29,6 @@ import com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard;
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard; import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard;
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
import com.fastasyncworldedit.core.internal.exception.FaweException;
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream; import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.limit.FaweLimit;
import com.fastasyncworldedit.core.util.ImgurUtility; import com.fastasyncworldedit.core.util.ImgurUtility;
@ -347,6 +346,8 @@ public class ClipboardCommands {
boolean atOrigin, boolean atOrigin,
@Switch(name = 's', desc = "Select the region after pasting") @Switch(name = 's', desc = "Select the region after pasting")
boolean selectPasted, boolean selectPasted,
@Switch(name = 'n', desc = "No paste, select only. (Implies -s)")
boolean onlySelect,
@Switch(name = 'e', desc = "Paste entities if available") @Switch(name = 'e', desc = "Paste entities if available")
boolean pasteEntities, boolean pasteEntities,
@Switch(name = 'b', desc = "Paste biomes if available") @Switch(name = 'b', desc = "Paste biomes if available")
@ -358,10 +359,12 @@ public class ClipboardCommands {
final BlockVector3 to = atOrigin ? origin : session.getPlacementPosition(actor); final BlockVector3 to = atOrigin ? origin : session.getPlacementPosition(actor);
checkPaste(actor, editSession, to, holder, clipboard); checkPaste(actor, editSession, to, holder, clipboard);
clipboard.paste(editSession, to, !ignoreAirBlocks, pasteEntities, pasteBiomes); if (!onlySelect) {
clipboard.paste(editSession, to, !ignoreAirBlocks, pasteEntities, pasteBiomes);
}
Region region = clipboard.getRegion().clone(); Region region = clipboard.getRegion().clone();
if (selectPasted) { if (selectPasted || onlySelect) {
BlockVector3 clipboardOffset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin()); BlockVector3 clipboardOffset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
BlockVector3 realTo = to.add(holder.getTransform().apply(clipboardOffset.toVector3()).toBlockPoint()); BlockVector3 realTo = to.add(holder.getTransform().apply(clipboardOffset.toVector3()).toBlockPoint());
BlockVector3 max = realTo.add(holder BlockVector3 max = realTo.add(holder
@ -373,7 +376,11 @@ public class ClipboardCommands {
selector.learnChanges(); selector.learnChanges();
selector.explainRegionAdjust(actor, session); selector.explainRegionAdjust(actor, session);
} }
actor.print(Caption.of("fawe.worldedit.paste.command.paste", to)); if (onlySelect) {
actor.print(Caption.of("worldedit.paste.selected"));
} else {
actor.print(Caption.of("fawe.worldedit.paste.command.paste", to));
}
if (!actor.hasPermission("fawe.tips")) { if (!actor.hasPermission("fawe.tips")) {
actor.print(Caption.of("fawe.tips.tip.copypaste")); actor.print(Caption.of("fawe.tips.tip.copypaste"));
@ -410,7 +417,7 @@ public class ClipboardCommands {
ClipboardHolder holder = session.getClipboard(); ClipboardHolder holder = session.getClipboard();
//FAWE start - use place //FAWE start - use place
if (holder.getTransform().isIdentity() && sourceMask == null) { if (holder.getTransform().isIdentity() && sourceMask == null) {
place(actor, world, session, editSession, ignoreAirBlocks, atOrigin, selectPasted, place(actor, world, session, editSession, ignoreAirBlocks, atOrigin, selectPasted, onlySelect,
pasteEntities, pasteBiomes pasteEntities, pasteBiomes
); );
return; return;

View File

@ -201,7 +201,7 @@ public class HistorySubCommands {
.at(summary.maxX, world.getMaxY(), summary.maxZ) .at(summary.maxX, world.getMaxY(), summary.maxZ)
); );
rollback.setTime(historyFile.lastModified()); rollback.setTime(historyFile.lastModified());
RollbackDatabase db = DBHandler.IMP RollbackDatabase db = DBHandler.dbHandler()
.getDatabase(world); .getDatabase(world);
db.logEdit(rollback); db.logEdit(rollback);
actor.print(TextComponent.of("Logging: " + historyFile)); actor.print(TextComponent.of("Logging: " + historyFile));