3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-10-03 12:11:04 +02:00

Initial experimental work on Folia support

Dieser Commit ist enthalten in:
SirYwell 2023-03-07 14:36:58 +01:00 committet von Phillipp Glanz
Ursprung 11069ee34b
Commit 0f2d710fd0
11 geänderte Dateien mit 347 neuen und 17 gelöschten Zeilen

Datei anzeigen

@ -11,6 +11,8 @@ import com.fastasyncworldedit.bukkit.regions.ResidenceFeature;
import com.fastasyncworldedit.bukkit.regions.TownyFeature;
import com.fastasyncworldedit.bukkit.regions.WorldGuardFeature;
import com.fastasyncworldedit.bukkit.util.BukkitTaskManager;
import com.fastasyncworldedit.bukkit.util.FoliaTaskManager;
import com.fastasyncworldedit.core.util.FoliaSupport;
import com.fastasyncworldedit.bukkit.util.ItemUtil;
import com.fastasyncworldedit.bukkit.util.MinecraftVersion;
import com.fastasyncworldedit.bukkit.util.image.BukkitImageViewer;
@ -63,6 +65,7 @@ public class FaweBukkit implements IFawe, Listener {
private ItemUtil itemUtil;
private Preloader preloader;
private volatile boolean keepUnloaded;
private static final Thread startingThread = Thread.currentThread();
public FaweBukkit(Plugin plugin) {
this.plugin = plugin;
@ -74,7 +77,7 @@ public class FaweBukkit implements IFawe, Listener {
} catch (Throwable e) {
LOGGER.error("Brush Listener Failed", e);
}
if (PaperLib.isPaper() && Settings.settings().EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING > 1) {
if (!FoliaSupport.isFolia() && PaperLib.isPaper() && Settings.settings().EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING > 1) {
new RenderListener(plugin);
}
} catch (final Throwable e) {
@ -89,20 +92,22 @@ public class FaweBukkit implements IFawe, Listener {
platformAdapter = new NMSAdapter();
//PlotSquared support is limited to Spigot/Paper as of 02/20/2020
TaskManager.taskManager().later(this::setupPlotSquared, 0);
// TODO plotsquared support
// TaskManager.taskManager().later(this::setupPlotSquared, 0);
// TODO moved out of task below??
Bukkit.getPluginManager().registerEvents(FaweBukkit.this, FaweBukkit.this.plugin);
// Registered delayed Event Listeners
TaskManager.taskManager().task(() -> {
/*TaskManager.taskManager().task(() -> {
// Fix for ProtocolSupport
Settings.settings().PROTOCOL_SUPPORT_FIX =
Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport");
// This class
Bukkit.getPluginManager().registerEvents(FaweBukkit.this, FaweBukkit.this.plugin);
// The tick limiter
new ChunkListener9();
});
});*/
// Warn if small-edits are enabled with extended world heights
if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) {
@ -192,6 +197,9 @@ public class FaweBukkit implements IFawe, Listener {
*/
@Override
public TaskManager getTaskManager() {
if (FoliaSupport.isFolia()) {
return new FoliaTaskManager();
}
return new BukkitTaskManager(plugin);
}
@ -312,6 +320,14 @@ public class FaweBukkit implements IFawe, Listener {
return platformAdapter;
}
@Override
public boolean isTickThread() {
if (FoliaSupport.isFolia()) {
return FoliaSupport.isTickThread();
}
return Thread.currentThread() == startingThread;
}
private void setupPlotSquared() {
Plugin plotSquared = this.plugin.getServer().getPluginManager().getPlugin("PlotSquared");
if (plotSquared == null) {

Datei anzeigen

@ -1,6 +1,7 @@
package com.fastasyncworldedit.bukkit.listener;
import com.fastasyncworldedit.bukkit.FaweBukkit;
import com.fastasyncworldedit.core.util.FoliaSupport;
import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.util.FaweTimer;
@ -59,6 +60,9 @@ public abstract class ChunkListener implements Listener {
Settings.settings().TICK_LIMITER.FALLING, Settings.settings().TICK_LIMITER.ITEMS};
public ChunkListener() {
if (FoliaSupport.isFolia()) {
return;
}
if (Settings.settings().TICK_LIMITER.ENABLED) {
PluginManager plm = Bukkit.getPluginManager();
Plugin plugin = Fawe.<FaweBukkit>platform().getPlugin();

Datei anzeigen

@ -1,10 +1,14 @@
package com.fastasyncworldedit.bukkit.util;
import com.fastasyncworldedit.core.util.TaskManager;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.util.Location;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.util.function.Supplier;
public class BukkitTaskManager extends TaskManager {
@ -34,11 +38,21 @@ public class BukkitTaskManager extends TaskManager {
this.plugin.getServer().getScheduler().runTask(this.plugin, runnable).getTaskId();
}
@Override
public void task(@NotNull final Runnable runnable, @NotNull final Location context) {
}
@Override
public void later(@Nonnull final Runnable runnable, final int delay) {
this.plugin.getServer().getScheduler().runTaskLater(this.plugin, runnable, delay).getTaskId();
}
@Override
public void later(@NotNull final Runnable runnable, final Location location, final int delay) {
}
@Override
public void laterAsync(@Nonnull final Runnable runnable, final int delay) {
this.plugin.getServer().getScheduler().runTaskLaterAsynchronously(this.plugin, runnable, delay);
@ -51,4 +65,16 @@ public class BukkitTaskManager extends TaskManager {
}
}
// TODO
@Override
public <T> T syncAt(final Supplier<T> supplier, final Location context) {
return sync(supplier);
}
@Override
public <T> T syncWith(final Supplier<T> supplier, final Player context) {
return sync(supplier);
}
}

Datei anzeigen

@ -0,0 +1,206 @@
package com.fastasyncworldedit.bukkit.util;
import com.fastasyncworldedit.core.util.TaskManager;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.util.Location;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import static java.lang.invoke.MethodHandles.dropReturn;
import static java.lang.invoke.MethodHandles.explicitCastArguments;
import static java.lang.invoke.MethodHandles.filterArguments;
import static java.lang.invoke.MethodHandles.insertArguments;
import static java.lang.invoke.MethodType.methodType;
public class FoliaTaskManager extends TaskManager {
private final ScheduledExecutorService backgroundExecutor = Executors.newSingleThreadScheduledExecutor();
private final AtomicInteger idCounter = new AtomicInteger();
@Override
public int repeat(@NotNull final Runnable runnable, final int interval) {
return fail();
}
@Override
public int repeatAsync(@NotNull final Runnable runnable, final int interval) {
backgroundExecutor.scheduleAtFixedRate(runnable, 0, ticksToMs(interval), TimeUnit.MILLISECONDS);
return idCounter.getAndIncrement();
}
@Override
public void async(@NotNull final Runnable runnable) {
backgroundExecutor.submit(runnable);
}
@Override
public void task(@NotNull final Runnable runnable) {
fail();
}
@Override
public void task(@NotNull final Runnable runnable, @NotNull final Location context) {
SchedulerAdapter.executeForLocation(context, runnable);
}
@Override
public void later(@NotNull final Runnable runnable, final int delay) {
fail();
}
@Override
public void later(@NotNull final Runnable runnable, final Location location, final int delay) {
fail("Not implemented");
}
@Override
public void laterAsync(@NotNull final Runnable runnable, final int delay) {
backgroundExecutor.schedule(runnable, ticksToMs(delay), TimeUnit.MILLISECONDS);
}
@Override
public void cancel(final int task) {
fail("Not implemented");
}
@Override
public <T> T syncAt(final Supplier<T> supplier, final Location context) {
FutureTask<T> task = new FutureTask<>(supplier::get);
SchedulerAdapter.executeForLocation(context, task);
try {
return task.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@Override
public <T> T syncWith(final Supplier<T> supplier, final Player context) {
FutureTask<T> task = new FutureTask<>(supplier::get);
SchedulerAdapter.executeForEntity(context, task);
try {
return task.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
private int ticksToMs(int ticks) {
// 1 tick = 50ms
return ticks * 50;
}
private <T> T fail() {
return fail("No main thread present");
}
private <T> T fail(String message) {
throw new UnsupportedOperationException(message);
}
private static class SchedulerAdapter {
private static final MethodHandle EXECUTE_FOR_LOCATION;
private static final MethodHandle EXECUTE_FOR_PLAYER;
private static final MethodType LOCATION_EXECUTE_TYPE = methodType(
void.class,
Plugin.class,
org.bukkit.Location.class,
Runnable.class
);
private static final MethodType ENTITY_EXECUTE_TYPE = methodType(
boolean.class,
Plugin.class,
Runnable.class,
Runnable.class,
long.class
);
static {
final Plugin pluginInstance = WorldEditPlugin.getInstance();
final MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle executeForLocation;
MethodHandle executeForPlayer;
try {
Class<?> regionisedSchedulerClass = Class.forName("io.papermc.paper.threadedregions.scheduler.RegionisedScheduler");
final Method method = Bukkit.class.getDeclaredMethod("getRegionScheduler");
executeForLocation = lookup.findVirtual(
regionisedSchedulerClass,
"execute",
LOCATION_EXECUTE_TYPE
);
executeForLocation = executeForLocation.bindTo(method.invoke(null));
executeForLocation = executeForLocation.bindTo(pluginInstance);
Class<?> entitySchedulerClass = Class.forName("io.papermc.paper.threadedregions.scheduler.EntityScheduler");
executeForPlayer = lookup.findVirtual(
entitySchedulerClass,
"execute",
ENTITY_EXECUTE_TYPE
);
// (ES, P, R, R, L)Z (ES, R, R, L)Z
executeForPlayer = insertArguments(executeForPlayer, 1, pluginInstance);
// (ES, R1, R2, L)Z -> (ES, R1)Z
executeForPlayer = insertArguments(executeForPlayer, 2, null, 0);
// (ES, R1)Z -> (ES, R1)V
executeForPlayer = dropReturn(executeForPlayer);
MethodHandle getScheduler = lookup.findVirtual(
org.bukkit.entity.Entity.class,
"getScheduler",
methodType(entitySchedulerClass)
);
// (ES, R1)V -> (E, R1)V
executeForPlayer = filterArguments(executeForPlayer, 0, getScheduler);
MethodType finalType = methodType(void.class, org.bukkit.entity.Player.class, Runnable.class);
// (ES, R1)V -> (P, R1)V
executeForPlayer = explicitCastArguments(executeForPlayer, finalType);
} catch (Throwable throwable) {
throw new AssertionError(throwable);
}
EXECUTE_FOR_LOCATION = executeForLocation;
EXECUTE_FOR_PLAYER = executeForPlayer;
}
static void executeForLocation(Location location, Runnable task) {
try {
EXECUTE_FOR_LOCATION.invokeExact(BukkitAdapter.adapt(location), task);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable other) {
throw new RuntimeException(other);
}
}
static void executeForEntity(Player player, Runnable task) {
// TODO task might not be run if player retires
try {
EXECUTE_FOR_PLAYER.invokeExact(BukkitAdapter.adapt(player), task);
} catch (Error | RuntimeException e) {
throw e;
} catch (Throwable other) {
throw new RuntimeException(other);
}
}
}
}

Datei anzeigen

@ -161,7 +161,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
public void giveItem(BaseItemStack itemStack) {
final PlayerInventory inv = player.getInventory();
ItemStack newItem = BukkitAdapter.adapt(itemStack);
TaskManager.taskManager().sync(() -> {
TaskManager.taskManager().syncWith(() -> {
if (itemStack.getType().getId().equalsIgnoreCase(WorldEdit.getInstance().getConfiguration().wandItem)) {
inv.remove(newItem);
}
@ -183,7 +183,7 @@ public class BukkitPlayer extends AbstractPlayerActor {
}
player.updateInventory();
return null;
});
}, this);
}
//FAWE end
@ -240,14 +240,15 @@ public class BukkitPlayer extends AbstractPlayerActor {
}
org.bukkit.World finalWorld = world;
//FAWE end
return TaskManager.taskManager().sync(() -> player.teleport(new Location(
// TODO async teleport?
return TaskManager.taskManager().syncWith(() -> player.teleport(new Location(
finalWorld,
pos.getX(),
pos.getY(),
pos.getZ(),
yaw,
pitch
)));
)), this);
}
@Override

Datei anzeigen

@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.util.CachedTextureUtil;
import com.fastasyncworldedit.core.util.CleanTextureUtil;
import com.fastasyncworldedit.core.util.FaweTimer;
import com.fastasyncworldedit.core.util.FoliaSupport;
import com.fastasyncworldedit.core.util.MainUtil;
import com.fastasyncworldedit.core.util.MemUtil;
import com.fastasyncworldedit.core.util.RandomTextureUtil;
@ -129,14 +130,17 @@ public class Fawe {
this.timer = new FaweTimer();
// Delayed worldedit setup
TaskManager.taskManager().later(() -> {
// TODO support again
/*TaskManager.taskManager().later(() -> {
try {
WEManager.weManager().addManagers(Fawe.this.implementation.getMaskManagers());
} catch (Throwable ignored) {
}
}, 0);
}, 0);*/
if (!FoliaSupport.isFolia()) {
TaskManager.taskManager().repeat(timer, 1);
}
clipboardExecutor = new KeyQueuedExecutorService<>(new ThreadPoolExecutor(
1,
@ -206,10 +210,15 @@ public class Fawe {
}
}
@Deprecated
public static boolean isMainThread() {
return instance == null || instance.thread == Thread.currentThread();
}
public static boolean isTickThread() {
return instance == null || instance.implementation.isTickThread();
}
/**
* Non-api. Handles an input FAWE exception if not already handled, given the input boolean array.
* Looks at the {@link FaweException.Type} and decides what to do (rethrows if we want to attempt to show the error to the

Datei anzeigen

@ -49,4 +49,6 @@ public interface IFawe {
FAWEPlatformAdapterImpl getPlatformAdapter();
boolean isTickThread();
}

Datei anzeigen

@ -17,6 +17,7 @@ import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal;
import com.fastasyncworldedit.core.util.task.FaweForkJoinWorkerThreadFactory;
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
import com.google.common.util.concurrent.Futures;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World;
import java.lang.ref.WeakReference;
@ -89,13 +90,14 @@ public abstract class QueueHandler implements Trimable, Runnable {
private long allocate = 50;
protected QueueHandler() {
TaskManager.taskManager().repeat(this, 1);
// TODO make main thread independent
// TaskManager.taskManager().repeat(this, 1);
}
@Override
public void run() {
if (!Fawe.isMainThread()) {
throw new IllegalStateException("Not main thread");
if (!Fawe.isTickThread()) {
throw new IllegalStateException("Not ticking thread");
}
if (!syncTasks.isEmpty()) {
long currentAllocate = getAllocate();

Datei anzeigen

@ -0,0 +1,38 @@
package com.fastasyncworldedit.core.util;
public final class FoliaSupport {
private FoliaSupport() {
}
private static final boolean IS_FOLIA;
private static final Class<?> TICK_THREAD_CLASS;
static {
boolean isFolia = false;
try {
// Assume API is present
Class.forName("io.papermc.paper.threadedregions.scheduler.EntityScheduler");
isFolia = true;
} catch (Exception unused) {
}
IS_FOLIA = isFolia;
Class<?> tickThreadClass = String.class; // thread will never be instance of String
if (IS_FOLIA) {
try {
tickThreadClass = Class.forName("io.papermc.paper.util.TickThread");
} catch (ClassNotFoundException e) {
throw new AssertionError(e);
}
}
TICK_THREAD_CLASS = tickThreadClass;
}
public static boolean isFolia() {
return IS_FOLIA;
}
public static boolean isTickThread() {
return TICK_THREAD_CLASS.isInstance(Thread.currentThread());
}
}

Datei anzeigen

@ -4,7 +4,10 @@ import com.fastasyncworldedit.core.Fawe;
import com.fastasyncworldedit.core.configuration.Settings;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.fastasyncworldedit.core.util.task.RunnableVal;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.util.Location;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull;
@ -52,6 +55,7 @@ public abstract class TaskManager {
* @param runnable the task to run
* @param interval in ticks
*/
@Deprecated
public abstract int repeat(@Nonnull final Runnable runnable, final int interval);
/**
@ -75,7 +79,14 @@ public abstract class TaskManager {
*
* @param runnable the task to run
*/
@Deprecated
public abstract void task(@Nonnull final Runnable runnable);
/**
* Run a task on the main thread.
*
* @param runnable the task to run
*/
public abstract void task(@Nonnull final Runnable runnable, @Nonnull Location contextLocation);
/**
* Get the public ForkJoinPool.
@ -159,6 +170,7 @@ public abstract class TaskManager {
/**
* Disable async catching for a specific task.
*/
@Deprecated
public void runUnsafe(Runnable run) {
QueueHandler queue = Fawe.instance().getQueueHandler();
queue.startUnsafe(Fawe.isMainThread());
@ -191,6 +203,7 @@ public abstract class TaskManager {
*
* @param runnable the task to run
*/
@Deprecated
public void taskNowMain(@Nonnull final Runnable runnable) {
if (Fawe.isMainThread()) {
runnable.run();
@ -215,6 +228,7 @@ public abstract class TaskManager {
* @param runnable the task to run.
* @param async whether the task should run on the main thread
*/
@Deprecated
public void taskSoonMain(@Nonnull final Runnable runnable, boolean async) {
if (async) {
async(runnable);
@ -230,7 +244,9 @@ public abstract class TaskManager {
* @param runnable the task to run
* @param delay in ticks
*/
@Deprecated
public abstract void later(@Nonnull final Runnable runnable, final int delay);
public abstract void later(@Nonnull final Runnable runnable, Location location, final int delay);
/**
* Run a task later asynchronously.
@ -255,6 +271,7 @@ public abstract class TaskManager {
* @param task the task to run on each object
* @param whenDone when the object task completes
*/
@Deprecated
public <T> void objectTask(Collection<T> objects, final RunnableVal<T> task, final Runnable whenDone) {
final Iterator<T> iterator = objects.iterator();
task(new Runnable() {
@ -307,6 +324,7 @@ public abstract class TaskManager {
}
}
@Deprecated
public void taskWhenFree(@Nonnull Runnable run) {
if (Fawe.isMainThread()) {
run.run();
@ -320,6 +338,7 @@ public abstract class TaskManager {
* - Useful if you need to access something from the Bukkit API from another thread<br>
* - Usually wait time is around 25ms<br>
*/
@Deprecated
public <T> T syncWhenFree(@Nonnull final RunnableVal<T> function) {
if (Fawe.isMainThread()) {
function.run();
@ -337,6 +356,7 @@ public abstract class TaskManager {
* - Useful if you need to access something from the Bukkit API from another thread<br>
* - Usually wait time is around 25ms<br>
*/
@Deprecated
public <T> T syncWhenFree(@Nonnull final Supplier<T> supplier) {
if (Fawe.isMainThread()) {
return supplier.get();
@ -353,6 +373,7 @@ public abstract class TaskManager {
* - Useful if you need to access something from the Bukkit API from another thread<br>
* - Usually wait time is around 25ms
*/
@Deprecated
public <T> T sync(@Nonnull final RunnableVal<T> function) {
return sync((Supplier<T>) function);
}
@ -362,6 +383,7 @@ public abstract class TaskManager {
* - Useful if you need to access something from the Bukkit API from another thread<br>
* - Usually wait time is around 25ms<br>
*/
@Deprecated
public <T> T sync(final Supplier<T> function) {
if (Fawe.isMainThread()) {
return function.get();
@ -373,4 +395,8 @@ public abstract class TaskManager {
}
}
public abstract <T> T syncAt(Supplier<T> supplier, Location context);
public abstract <T> T syncWith(Supplier<T> supplier, Player context);
}

Datei anzeigen

@ -58,12 +58,12 @@ public class AsyncPlayer extends PlayerProxy {
@Override
public void findFreePosition() {
TaskManager.taskManager().sync(new RunnableVal<Boolean>() {
TaskManager.taskManager().syncWith(new RunnableVal<Boolean>() {
@Override
public void run(Boolean value) {
getBasePlayer().findFreePosition();
}
});
}, this);
}
@Override