package net.ME1312.SubServers.Client.Bukkit.Library.Compatibility; import net.ME1312.Galaxi.Library.Container.Container; import net.ME1312.Galaxi.Library.Try; import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.plugin.Plugin; import java.time.Duration; import java.time.Instant; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; public abstract class AgnosticScheduler { private static final boolean regional = Try.all.get(() -> Class.forName("io.papermc.paper.threadedregions.scheduler.RegionScheduler") != null, false); private static final Runnable empty = () -> {}; public static final AgnosticScheduler async = ((regional)? new AgnosticScheduler() { @Override public Runnable runs(Plugin plugin, Consumer task) { return Bukkit.getAsyncScheduler().runNow(plugin, t -> task.accept(t::cancel))::cancel; } @Override public Runnable runs(Plugin plugin, Consumer task, long delay) { return Bukkit.getAsyncScheduler().runDelayed(plugin, t -> task.accept(t::cancel), delay * 50, TimeUnit.MILLISECONDS)::cancel; } @Override public Runnable runs(Plugin plugin, Consumer task, long delay, TimeUnit units) { return Bukkit.getAsyncScheduler().runDelayed(plugin, t -> task.accept(t::cancel), delay, units)::cancel; } @Override public Runnable repeats(Plugin plugin, Consumer task, long repeat) { return repeats(plugin, task, repeat *= 50, repeat, TimeUnit.MILLISECONDS); } @Override public Runnable repeats(Plugin plugin, Consumer task, long repeat, TimeUnit units) { return repeats(plugin, task, repeat, repeat, units); } @Override public Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat) { return repeats(plugin, task, delay * 50, repeat * 50, TimeUnit.MILLISECONDS); } @Override public Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat, TimeUnit units) { if (repeat != 0) { return Bukkit.getAsyncScheduler().runAtFixedRate(plugin, t -> task.accept(t::cancel), delay, repeat, units)::cancel; } else if (units.ordinal() >= TimeUnit.MILLISECONDS.ordinal()) { return Bukkit.getAsyncScheduler().runAtFixedRate(plugin, t -> task.accept(t::cancel), units.toMillis(delay), 50, TimeUnit.MILLISECONDS)::cancel; } else { return Bukkit.getAsyncScheduler().runAtFixedRate(plugin, t -> task.accept(t::cancel), delay, units.convert(50, TimeUnit.MILLISECONDS), units)::cancel; } } } : new AgnosticScheduler() { @Override public Runnable runs(Plugin plugin, Consumer task) { final Container cancel = new Container<>(empty); return cancel.value = Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> task.accept(cancel.value))::cancel; } @Override public Runnable runs(Plugin plugin, Consumer task, long delay) { final Container cancel = new Container<>(empty); return cancel.value = Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> task.accept(cancel.value), check(delay, 0))::cancel; } @Override public Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat) { final Container cancel = new Container<>(empty); return cancel.value = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, () -> task.accept(cancel.value), check(delay, 0), check(repeat, 0))::cancel; } } ); public static final AgnosticScheduler global = ((regional)? new AgnosticScheduler() { @Override public Runnable runs(Plugin plugin, Consumer task) { return Bukkit.getGlobalRegionScheduler().run(plugin, t -> task.accept(t::cancel))::cancel; } @Override public Runnable runs(Plugin plugin, Consumer task, long delay) { return Bukkit.getGlobalRegionScheduler().runDelayed(plugin, t -> task.accept(t::cancel), check(delay, 1))::cancel; } @Override public Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat) { return Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, t -> task.accept(t::cancel), check(delay, 1), check(repeat, 1))::cancel; } } : new AgnosticScheduler() { @Override public Runnable runs(Plugin plugin, Consumer task) { final Container cancel = new Container<>(empty); return cancel.value = Bukkit.getScheduler().runTask(plugin, () -> task.accept(cancel.value))::cancel; } @Override public Runnable runs(Plugin plugin, Consumer task, long delay) { final Container cancel = new Container<>(empty); return cancel.value = Bukkit.getScheduler().runTaskLater(plugin, () -> task.accept(cancel.value), check(delay, 0))::cancel; } @Override public Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat) { final Container cancel = new Container<>(empty); return cancel.value = Bukkit.getScheduler().runTaskTimer(plugin, () -> task.accept(cancel.value), check(delay, 0), check(repeat, 0))::cancel; } } ); public static AgnosticScheduler at(Block block) { return atChunk(block.getWorld(), block.getX() >> 4, block.getZ() >> 4); } public static AgnosticScheduler at(Location location) { return atChunk(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); } public static AgnosticScheduler at(World world, int x, int z) { return atChunk(world, x >> 4, z >> 4); } public static AgnosticScheduler atChunk(Chunk chunk) { return atChunk(chunk.getWorld(), chunk.getX(), chunk.getZ()); } public static AgnosticScheduler atChunk(World world, int cx, int cz) { return (regional)? new AgnosticScheduler() { @Override public Runnable runs(Plugin plugin, Consumer task) { return Bukkit.getRegionScheduler().run(plugin, world, cx, cz, t -> task.accept(t::cancel))::cancel; } @Override public Runnable runs(Plugin plugin, Consumer task, long delay) { return Bukkit.getRegionScheduler().runDelayed(plugin, world, cx, cz, t -> task.accept(t::cancel), check(delay, 1))::cancel; } @Override public Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat) { return Bukkit.getRegionScheduler().runAtFixedRate(plugin, world, cx, cz, t -> task.accept(t::cancel), check(delay, 1), check(repeat, 1))::cancel; } } : global; } public static AgnosticScheduler following(Entity entity) { return (regional)? new AgnosticScheduler() { @Override public Runnable runs(Plugin plugin, Consumer task) { final io.papermc.paper.threadedregions.scheduler.ScheduledTask value = entity.getScheduler().run(plugin, t -> task.accept(t::cancel), () -> { at(entity.getLocation()).runs(plugin, task); }); return (value != null)? value::cancel : at(entity.getLocation()).runs(plugin, task); } @Override public Runnable runs(Plugin plugin, Consumer task, long delay) { return runs(plugin, task, delay * 50, delay); } @Override public Runnable runs(Plugin plugin, Consumer task, long delay, TimeUnit units) { return runs(plugin, task, delay = units.toMillis(delay), delay / 50); } private Runnable runs(Plugin plugin, Consumer task, long dMS, long dT) { if (dMS < 0) throw new IllegalStateException("Delay may not be < 0"); final Instant next = Instant.now().plusMillis(dMS); final io.papermc.paper.threadedregions.scheduler.ScheduledTask value = entity.getScheduler().runDelayed(plugin, t -> task.accept(t::cancel), () -> { at(entity.getLocation()).runs(plugin, task, Math.min(0, Duration.between(Instant.now(), next).toMillis()), TimeUnit.MILLISECONDS); }, (dT == 0)? 1 : dT); return (value != null)? value::cancel : at(entity.getLocation()).runs(plugin, task, dT); } @Override public Runnable repeats(Plugin plugin, Consumer task, long repeat) { final long ms; return repeats(plugin, task, ms = repeat * 50, repeat, ms, repeat); } @Override public Runnable repeats(Plugin plugin, Consumer task, long repeat, TimeUnit units) { final long ticks; return repeats(plugin, task, repeat = units.toMillis(repeat), ticks = repeat / 50, repeat, ticks); } @Override public Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat) { return repeats(plugin, task, delay * 50, delay, repeat * 50, repeat); } @Override public Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat, TimeUnit units) { return repeats(plugin, task, delay = units.toMillis(delay), delay / 50, repeat = units.toMillis(repeat), repeat / 50); } private Runnable repeats(Plugin plugin, Consumer task, long dMS, long dT, long rMS, long rT) { if (dMS < 0 || rMS < 0) throw new IllegalStateException("Delay may not be < 0"); final Container next = new Container<>(Instant.now().plusMillis(dMS)); final io.papermc.paper.threadedregions.scheduler.ScheduledTask value = entity.getScheduler().runAtFixedRate(plugin, t -> { next.value = Instant.now().plusMillis(rMS); task.accept(t::cancel); }, () -> { at(entity.getLocation()).repeats(plugin, task, Math.min(0, Duration.between(Instant.now(), next.value).toMillis()), rMS, TimeUnit.MILLISECONDS); }, (dT == 0)? 1 : dT, (rT == 0)? 1 : rT); return (value != null)? value::cancel : at(entity.getLocation()).repeats(plugin, task, dT, rT); } } : global; } private static long check(long amount, int minimum) { if (amount < 0) throw new IllegalArgumentException("Delay ticks may not be < 0"); return (amount == 0)? minimum : amount; } public abstract Runnable runs(Plugin plugin, Consumer task); public abstract Runnable runs(Plugin plugin, Consumer task, long delay); public Runnable runs(Plugin plugin, Consumer task, long delay, TimeUnit units) { return runs(plugin, task, units.toMillis(delay) / 50); } public Runnable repeats(Plugin plugin, Consumer task, long repeat) { return repeats(plugin, task, repeat, repeat); } public Runnable repeats(Plugin plugin, Consumer task, long repeat, TimeUnit units) { return repeats(plugin, task, repeat = units.toMillis(repeat) / 50, repeat); } public abstract Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat); public Runnable repeats(Plugin plugin, Consumer task, long delay, long repeat, TimeUnit units) { return repeats(plugin, task, units.toMillis(delay) / 50, units.toMillis(repeat) / 50); } }