3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-12-26 11:00:04 +01:00
Dieser Commit ist enthalten in:
SirYwell 2023-12-11 21:27:45 +01:00
Ursprung d64b0d9edf
Commit fbe69bdc81
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
16 geänderte Dateien mit 244 neuen und 111 gelöschten Zeilen

Datei anzeigen

@ -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 = {}) {

Datei anzeigen

@ -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;

Datei anzeigen

@ -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);

Datei anzeigen

@ -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() {

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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();

Datei anzeigen

@ -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

Datei anzeigen

@ -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

Datei anzeigen

@ -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);

Datei anzeigen

@ -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

Datei anzeigen

@ -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

Datei anzeigen

@ -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);
}

Datei anzeigen

@ -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

Datei anzeigen

@ -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);
}
}
}

Datei anzeigen

@ -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>

Datei anzeigen

@ -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);