Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-12-26 11:00:04 +01:00
rebase fixes
Dieser Commit ist enthalten in:
Ursprung
d64b0d9edf
Commit
fbe69bdc81
@ -85,7 +85,7 @@ allprojects {
|
||||
|
||||
applyCommonConfiguration()
|
||||
val supportedVersions = listOf("1.18.2", "1.19.4", "1.20", "1.20.4")
|
||||
val foliaSupportedVersions = listOf("1.20.1")
|
||||
val foliaSupportedVersions = listOf("1.20.2")
|
||||
|
||||
tasks {
|
||||
fun registerVersion(version: String, software: String, task: RunServer.() -> Unit = {}) {
|
||||
|
@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -35,82 +35,21 @@ public class PaperweightStarlightRelighter extends StarlightRelighter<ServerLeve
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
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
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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()));
|
||||
}
|
||||
if (FoliaSupport.isFolia()) {
|
||||
relightRegion(andThen, coords);
|
||||
return;
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
relightRegion(andThen, coords);
|
||||
});
|
||||
}
|
||||
|
||||
private void relightRegion(Runnable andThen, Set<ChunkPos> coords) {
|
||||
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
|
||||
))
|
||||
);
|
||||
}
|
||||
Location location = toLocation(coords.iterator().next());
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAcceptAsync(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), location);
|
||||
// call callback on our own threads
|
||||
TaskManager.taskManager().async(andThen);
|
||||
}
|
||||
),
|
||||
task -> TaskManager.taskManager().task(task, location)
|
||||
);
|
||||
}
|
||||
|
||||
private Location toLocation(ChunkPos chunkPos) {
|
||||
return PaperweightPlatformAdapter.toLocation(this.serverLevel, chunkPos);
|
||||
}
|
||||
|
||||
private void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
@Override
|
||||
protected void invokeRelight(
|
||||
final Set<ChunkPos> coords,
|
||||
final Consumer<ChunkPos> chunkCallback,
|
||||
final IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
||||
|
@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
@ -21,6 +22,7 @@ import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
@ -58,7 +60,10 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
this.level = level;
|
||||
// Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
|
||||
// - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
|
||||
this.lastTick = new AtomicInteger(MinecraftServer.currentTick);
|
||||
this.lastTick = new AtomicInteger();
|
||||
if (!FoliaSupport.isFolia()) {
|
||||
this.lastTick.set(Bukkit.getCurrentTick());
|
||||
}
|
||||
}
|
||||
|
||||
private Level getLevel() {
|
||||
|
@ -319,9 +319,11 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
// Ensure chunk is definitely loaded before applying a ticket
|
||||
io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||
PaperweightPlatformAdapter.task(() -> serverLevel
|
||||
.getChunkSource()
|
||||
.addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||
.addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE),
|
||||
serverLevel, chunkX, chunkZ
|
||||
);
|
||||
}
|
||||
|
||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||
|
@ -2,7 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
@ -15,12 +15,13 @@ import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
@ -58,7 +59,10 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
this.level = level;
|
||||
// Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
|
||||
// - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
|
||||
this.lastTick = new AtomicInteger(MinecraftServer.currentTick);
|
||||
this.lastTick = new AtomicInteger();
|
||||
if (!FoliaSupport.isFolia()) {
|
||||
this.lastTick.set(Bukkit.getCurrentTick());
|
||||
}
|
||||
}
|
||||
|
||||
private Level getLevel() {
|
||||
@ -250,7 +254,8 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
}
|
||||
}
|
||||
};
|
||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
// TODO
|
||||
// TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -269,7 +274,8 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<Level
|
||||
if (Fawe.isMainThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
TaskManager.taskManager().sync(runnableVal);
|
||||
// TODO
|
||||
// TaskManager.taskManager().sync(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
|
@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
@ -58,7 +59,6 @@ import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_20_R3.CraftChunk;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -298,7 +298,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
} catch (TimeoutException e) {
|
||||
String world = serverLevel.getWorld().getName();
|
||||
// We've already taken 10 seconds we can afford to wait a little here.
|
||||
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
||||
boolean loaded = TaskManager.taskManager().syncGlobal(() -> Bukkit.getWorld(world) != null);
|
||||
if (loaded) {
|
||||
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
||||
// Retry chunk load
|
||||
@ -313,7 +313,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||
return TaskManager.taskManager().syncGlobal(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
@ -374,7 +374,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
);
|
||||
}
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
});
|
||||
}, BukkitAdapter.adapt(nmsWorld.getWorld()), chunkX, chunkZ);
|
||||
}
|
||||
|
||||
private static List<ServerPlayer> nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) {
|
||||
@ -674,6 +674,10 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
public static void task(Runnable task, ServerLevel level, int chunkX, int chunkZ) {
|
||||
TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ);
|
||||
}
|
||||
|
||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||
|
||||
@Override
|
||||
|
@ -4,13 +4,13 @@ import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightPlatformAdapter;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
@ -438,11 +438,11 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> {
|
||||
PaperweightPlatformAdapter.task(() -> {
|
||||
final CraftWorld world = freshWorld.getWorld();
|
||||
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
|
||||
blockPopulator.populate(world, random, chunk);
|
||||
});
|
||||
}, freshWorld, levelChunk.getPos().x, levelChunk.getPos().z);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.fastasyncworldedit.bukkit.adapter;
|
||||
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
@ -11,7 +12,16 @@ import org.bukkit.TreeType;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.fastasyncworldedit.core.util.FoliaSupport.getRethrowing;
|
||||
import static com.fastasyncworldedit.core.util.FoliaSupport.runRethrowing;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
/**
|
||||
* A base class for version-specific implementations of the BukkitImplAdapter
|
||||
@ -21,6 +31,39 @@ import java.util.List;
|
||||
*/
|
||||
public abstract class FaweAdapter<TAG, SERVER_LEVEL> extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter<TAG> {
|
||||
|
||||
private static final VarHandle CAPTURE_TREE_GENERATION;
|
||||
private static final VarHandle CAPTURE_BLOCK_STATES;
|
||||
private static final MethodHandle CAPTURED_BLOCK_STATES;
|
||||
private static final MethodHandle GET_CURRENT_WORLD_DATA;
|
||||
|
||||
static {
|
||||
VarHandle captureTreeGeneration = null;
|
||||
VarHandle captureBlockStates = null;
|
||||
MethodHandle capturedBlockStates = null;
|
||||
MethodHandle getCurrentWorldData = null;
|
||||
if (FoliaSupport.isFolia()) {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
try {
|
||||
Class<?> regionizedWorldDataClass = Class.forName("io.papermc.paper.threadedregions.RegionizedWorldData");
|
||||
Class<?> serverLevelClass = regionizedWorldDataClass.getDeclaredField("world").getType();
|
||||
captureTreeGeneration = lookup.findVarHandle(regionizedWorldDataClass, "captureTreeGeneration", boolean.class);
|
||||
captureBlockStates = lookup.findVarHandle(regionizedWorldDataClass, "captureBlockStates", boolean.class);
|
||||
capturedBlockStates = lookup.findGetter(regionizedWorldDataClass, "capturedBlockStates", Map.class);
|
||||
getCurrentWorldData = lookup.findVirtual(
|
||||
serverLevelClass,
|
||||
"getCurrentWorldData",
|
||||
methodType(regionizedWorldDataClass)
|
||||
);
|
||||
} catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new AssertionError("Incompatible Folia version", e);
|
||||
}
|
||||
}
|
||||
CAPTURE_TREE_GENERATION = captureTreeGeneration;
|
||||
CAPTURE_BLOCK_STATES = captureBlockStates;
|
||||
CAPTURED_BLOCK_STATES = capturedBlockStates;
|
||||
GET_CURRENT_WORLD_DATA = getCurrentWorldData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateTree(
|
||||
final TreeGenerator.TreeType treeType,
|
||||
@ -35,17 +78,17 @@ public abstract class FaweAdapter<TAG, SERVER_LEVEL> extends CachedBukkitAdapter
|
||||
}
|
||||
BlockVector3 target = blockVector3;
|
||||
SERVER_LEVEL serverLevel = getServerLevel(world);
|
||||
List<BlockState> placed = TaskManager.taskManager().sync(() -> {
|
||||
preCaptureStates(serverLevel);
|
||||
List<BlockState> placed = TaskManager.taskManager().syncAt(() -> {
|
||||
preCaptureStatesCommon(serverLevel);
|
||||
try {
|
||||
if (!world.generateTree(BukkitAdapter.adapt(world, target), bukkitType)) {
|
||||
return null;
|
||||
}
|
||||
return getCapturedBlockStatesCopy(serverLevel);
|
||||
return getCapturedBlockStatesCopyCommon(serverLevel);
|
||||
} finally {
|
||||
postCaptureBlockStates(serverLevel);
|
||||
postCaptureBlockStatesCommon(serverLevel);
|
||||
}
|
||||
});
|
||||
}, BukkitAdapter.adapt(world), blockVector3.getBlockX() >> 4, blockVector3.getBlockZ() >> 4);
|
||||
|
||||
if (placed == null || placed.isEmpty()) {
|
||||
return false;
|
||||
@ -61,6 +104,57 @@ public abstract class FaweAdapter<TAG, SERVER_LEVEL> extends CachedBukkitAdapter
|
||||
return true;
|
||||
}
|
||||
|
||||
private void preCaptureStatesCommon(SERVER_LEVEL serverLevel) {
|
||||
if (FoliaSupport.isFolia()) {
|
||||
preCaptureStatesFolia(serverLevel);
|
||||
} else {
|
||||
preCaptureStates(serverLevel);
|
||||
}
|
||||
}
|
||||
|
||||
private List<BlockState> getCapturedBlockStatesCopyCommon(SERVER_LEVEL serverLevel) {
|
||||
if (FoliaSupport.isFolia()) {
|
||||
return getCapturedBlockStatesCopyFolia(serverLevel);
|
||||
} else {
|
||||
return getCapturedBlockStatesCopy(serverLevel);
|
||||
}
|
||||
}
|
||||
|
||||
private void postCaptureBlockStatesCommon(SERVER_LEVEL serverLevel) {
|
||||
if (FoliaSupport.isFolia()) {
|
||||
postCaptureBlockStatesFolia(serverLevel);
|
||||
} else {
|
||||
postCaptureBlockStates(serverLevel);
|
||||
}
|
||||
}
|
||||
|
||||
private void preCaptureStatesFolia(SERVER_LEVEL serverLevel) {
|
||||
runRethrowing(() -> {
|
||||
Object currentWorldData = GET_CURRENT_WORLD_DATA.invoke(serverLevel);
|
||||
CAPTURE_TREE_GENERATION.set(currentWorldData, true);
|
||||
CAPTURE_BLOCK_STATES.set(currentWorldData, true);
|
||||
});
|
||||
}
|
||||
|
||||
private List<BlockState> getCapturedBlockStatesCopyFolia(SERVER_LEVEL serverLevel) {
|
||||
return getRethrowing(() -> {
|
||||
Object currentWorldData = GET_CURRENT_WORLD_DATA.invoke(serverLevel);
|
||||
@SuppressWarnings("unchecked")
|
||||
var capturedBlockStates = (Map<?, BlockState>) CAPTURED_BLOCK_STATES.invoke(currentWorldData);
|
||||
return new ArrayList<>(capturedBlockStates.values());
|
||||
});
|
||||
}
|
||||
|
||||
private void postCaptureBlockStatesFolia(SERVER_LEVEL serverLevel) {
|
||||
runRethrowing(() -> {
|
||||
Object currentWorldData = GET_CURRENT_WORLD_DATA.invoke(serverLevel);
|
||||
CAPTURE_TREE_GENERATION.set(currentWorldData, false);
|
||||
CAPTURE_BLOCK_STATES.set(currentWorldData, false);
|
||||
var capturedBlockStates = (Map<?, ?>) CAPTURED_BLOCK_STATES.invoke(currentWorldData);
|
||||
capturedBlockStates.clear();
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract void preCaptureStates(SERVER_LEVEL serverLevel);
|
||||
|
||||
protected abstract List<BlockState> getCapturedBlockStatesCopy(SERVER_LEVEL serverLevel);
|
||||
|
@ -78,7 +78,8 @@ public abstract class StarlightRelighter<SERVER_LEVEL, CHUNK_POS> implements Rel
|
||||
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
|
||||
}
|
||||
// post process chunks on main thread
|
||||
TaskManager.taskManager().task(() -> postProcessChunks(coords));
|
||||
// safe to run globally on Folia as ticket modification is behind a lock
|
||||
TaskManager.taskManager().taskGlobal(() -> postProcessChunks(coords));
|
||||
// call callback on our own threads
|
||||
TaskManager.taskManager().async(andThen);
|
||||
};
|
||||
@ -99,7 +100,7 @@ public abstract class StarlightRelighter<SERVER_LEVEL, CHUNK_POS> implements Rel
|
||||
*/
|
||||
protected void fixLighting(LongSet chunks, Runnable andThen) {
|
||||
Set<CHUNK_POS> coords = convertChunkKeysToChunkPos(chunks);
|
||||
TaskManager.taskManager().task(() -> {
|
||||
TaskManager.taskManager().taskGlobal(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
List<CompletableFuture<?>> futures = chunkLoadFutures(coords);
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
|
@ -33,17 +33,22 @@ public class BukkitTaskManager extends TaskManager {
|
||||
|
||||
@Override
|
||||
public void task(@NotNull final Runnable runnable, @NotNull final Location location) {
|
||||
|
||||
this.plugin.getServer().getScheduler().runTask(this.plugin, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void task(@NotNull final Runnable runnable, @NotNull final World world, final int chunkX, final int chunkZ) {
|
||||
this.plugin.getServer().getScheduler().runTask(this.plugin, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void taskGlobal(final Runnable runnable) {
|
||||
this.plugin.getServer().getGlobalRegionScheduler().run(this.plugin, task -> runnable.run());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void later(@NotNull final Runnable runnable, final Location location, final int delay) {
|
||||
|
||||
this.plugin.getServer().getScheduler().runTaskLater(this.plugin, runnable, delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.fastasyncworldedit.bukkit.util;
|
||||
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
@ -9,6 +10,7 @@ import com.sk89q.worldedit.world.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -16,8 +18,21 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.lookup;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class FoliaTaskManager extends TaskManager {
|
||||
|
||||
private final static MethodHandle IS_GLOBAL_TICK_THREAD;
|
||||
|
||||
static {
|
||||
try {
|
||||
IS_GLOBAL_TICK_THREAD = lookup().findStatic(Bukkit.class, "isGlobalTickThread", methodType(boolean.class));
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new AssertionError("Incompatile Folia version", e);
|
||||
}
|
||||
}
|
||||
|
||||
private final AtomicInteger idCounter = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
@ -49,6 +64,11 @@ public class FoliaTaskManager extends TaskManager {
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void taskGlobal(final Runnable runnable) {
|
||||
Bukkit.getGlobalRegionScheduler().run(WorldEditPlugin.getInstance(), asConsumer(runnable));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void later(@NotNull final Runnable runnable, final Location location, final int delay) {
|
||||
Bukkit.getRegionScheduler().runDelayed(
|
||||
@ -80,6 +100,7 @@ public class FoliaTaskManager extends TaskManager {
|
||||
if (Bukkit.isOwnedByCurrentRegion(adapt, chunkX, chunkZ)) {
|
||||
return supplier.get();
|
||||
}
|
||||
ensureOffTickThread();
|
||||
FutureTask<T> task = new FutureTask<>(supplier::get);
|
||||
Bukkit.getRegionScheduler().run(
|
||||
WorldEditPlugin.getInstance(),
|
||||
@ -105,6 +126,7 @@ public class FoliaTaskManager extends TaskManager {
|
||||
if (Bukkit.isOwnedByCurrentRegion(adapt)) {
|
||||
return supplier.get();
|
||||
}
|
||||
ensureOffTickThread();
|
||||
FutureTask<T> task = new FutureTask<>(supplier::get);
|
||||
adapt.getScheduler().execute(WorldEditPlugin.getInstance(), task, null, 0);
|
||||
try {
|
||||
@ -117,6 +139,9 @@ public class FoliaTaskManager extends TaskManager {
|
||||
@Override
|
||||
public <T> T syncGlobal(final Supplier<T> supplier) {
|
||||
// TODO avoid deadlocks (Bukkit.isGlobalTickThread not available at time of writing)
|
||||
if (isGlobalTickThread()) {
|
||||
return supplier.get();
|
||||
}
|
||||
FutureTask<T> task = new FutureTask<>(supplier::get);
|
||||
Bukkit.getGlobalRegionScheduler().run(WorldEditPlugin.getInstance(), asConsumer(task));
|
||||
try {
|
||||
@ -126,12 +151,21 @@ public class FoliaTaskManager extends TaskManager {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGlobalTickThread() {
|
||||
return FoliaSupport.getRethrowing(() -> (boolean) IS_GLOBAL_TICK_THREAD.invokeExact());
|
||||
}
|
||||
|
||||
private void ensureOffTickThread() {
|
||||
if (FoliaSupport.isTickThread()) {
|
||||
throw new IllegalStateException("Expected to be off tick thread");
|
||||
}
|
||||
}
|
||||
|
||||
private int ticksToMs(int ticks) {
|
||||
// 1 tick = 50ms
|
||||
return ticks * 50;
|
||||
}
|
||||
|
||||
|
||||
private <T> T fail(String message) {
|
||||
throw new UnsupportedOperationException(message);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ package com.sk89q.worldedit.bukkit;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Caption;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.util.FoliaSupport;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.util.StringUtil;
|
||||
import com.sk89q.wepif.VaultResolver;
|
||||
@ -52,6 +53,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.gamemode.GameMode;
|
||||
import com.sk89q.worldedit.world.gamemode.GameModes;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@ -69,7 +71,9 @@ import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class BukkitPlayer extends AbstractPlayerActor {
|
||||
|
||||
@ -239,16 +243,21 @@ public class BukkitPlayer extends AbstractPlayerActor {
|
||||
}
|
||||
}
|
||||
org.bukkit.World finalWorld = world;
|
||||
final Location target = new Location(finalWorld, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch);
|
||||
Supplier<CompletableFuture<Boolean>> teleport = () -> PaperLib.teleportAsync(player, target);
|
||||
if (FoliaSupport.isTickThread()) {
|
||||
teleport.get().whenComplete((b, thr) -> {
|
||||
if (thr != null) {
|
||||
thr.printStackTrace();
|
||||
}
|
||||
if (!b) {
|
||||
player.sendMessage("Teleportation failed");
|
||||
}
|
||||
});
|
||||
return true; // TODO this might not be correct
|
||||
}
|
||||
return TaskManager.taskManager().syncWith(() -> teleport.get().join(), this);
|
||||
//FAWE end
|
||||
// TODO async teleport?
|
||||
return TaskManager.taskManager().syncWith(() -> player.teleport(new Location(
|
||||
finalWorld,
|
||||
pos.getX(),
|
||||
pos.getY(),
|
||||
pos.getZ(),
|
||||
yaw,
|
||||
pitch
|
||||
)), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,4 +35,35 @@ public final class FoliaSupport {
|
||||
public static boolean isTickThread() {
|
||||
return TICK_THREAD_CLASS.isInstance(Thread.currentThread());
|
||||
}
|
||||
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowingSupplier<T> {
|
||||
|
||||
T get() throws Throwable;
|
||||
|
||||
}
|
||||
@FunctionalInterface
|
||||
public interface ThrowingRunnable {
|
||||
|
||||
void run() throws Throwable;
|
||||
|
||||
}
|
||||
|
||||
public static void runRethrowing(ThrowingRunnable runnable) {
|
||||
getRethrowing(() -> {
|
||||
runnable.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> T getRethrowing(ThrowingSupplier<T> supplier) {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ public abstract class TaskManager {
|
||||
|
||||
public abstract void task(@Nonnull final Runnable runnable, @Nonnull World world, int chunkX, int chunkZ);
|
||||
|
||||
public abstract void taskGlobal(Runnable runnable);
|
||||
|
||||
/**
|
||||
* Get the public ForkJoinPool.
|
||||
* - ONLY SUBMIT SHORT LIVED TASKS<br>
|
||||
|
@ -40,7 +40,7 @@ public class AsyncPlayer extends PlayerProxy {
|
||||
|
||||
@Override
|
||||
public void findFreePosition(Location searchPos) {
|
||||
TaskManager.taskManager().syncAt(new RunnableVal<Boolean>() {
|
||||
TaskManager.taskManager().task(new RunnableVal<Boolean>() {
|
||||
@Override
|
||||
public void run(Boolean value) {
|
||||
getBasePlayer().findFreePosition(searchPos);
|
||||
@ -50,7 +50,7 @@ public class AsyncPlayer extends PlayerProxy {
|
||||
|
||||
@Override
|
||||
public void setOnGround(Location searchPos) {
|
||||
TaskManager.taskManager().syncAt(new RunnableVal<Boolean>() {
|
||||
TaskManager.taskManager().task(new RunnableVal<Boolean>() {
|
||||
@Override
|
||||
public void run(Boolean value) {
|
||||
getBasePlayer().setOnGround(searchPos);
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren