From 9ddedc75a16e126d0af7241231ea2935fe8ccba6 Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Tue, 20 Aug 2024 19:12:46 -0500 Subject: [PATCH] Use processing for wrecks (again) (#699) * Use processing for wrecks * Don't use Tick * Parallelize on chunks * Clamp y axis to 0 to avoid batching on sub-chunk * Fix axis modifier and add annotations * Add doc comments * Add more efficient NONE::andThen imp * Fix bugs * Update total duration to match previous and use floor * seconds to tick conversion * Fold repeated AndThen calls * load chunks * Fix 1.18.2 async chunk deadlock --------- Co-authored-by: ohnoey --- .../net/countercraft/movecraft/Movecraft.java | 8 ++ .../movecraft/async/AsyncManager.java | 92 +++---------------- .../movecraft/craft/CraftManager.java | 2 +- .../movecraft/features/fading/FadeTask.java | 42 +++++++++ .../features/fading/WreckManager.java | 45 +++++++++ .../movecraft/features/fading/WreckTask.java | 73 +++++++++++++++ .../processing/effects/DeferredEffect.java | 36 ++++++++ .../processing/effects/SetBlockEffect.java | 24 ++++- .../movecraft/MovecraftLocation.java | 20 ++++ .../movecraft/processing/WorldManager.java | 5 +- .../movecraft/processing/effects/Effect.java | 69 ++++++++++++-- .../movecraft/util/CollectorUtils.java | 16 ++++ .../movecraft/support/v1_18/IAsyncChunk.java | 6 +- 13 files changed, 344 insertions(+), 94 deletions(-) create mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/features/fading/FadeTask.java create mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckManager.java create mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckTask.java create mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/DeferredEffect.java create mode 100644 api/src/main/java/net/countercraft/movecraft/util/CollectorUtils.java diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java index d5f6eabbb..fe69859c1 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java @@ -27,6 +27,7 @@ import net.countercraft.movecraft.features.contacts.ContactsCommand; import net.countercraft.movecraft.features.contacts.ContactsManager; import net.countercraft.movecraft.features.contacts.ContactsSign; +import net.countercraft.movecraft.features.fading.WreckManager; import net.countercraft.movecraft.features.status.StatusManager; import net.countercraft.movecraft.features.status.StatusSign; import net.countercraft.movecraft.listener.*; @@ -57,6 +58,7 @@ public class Movecraft extends JavaPlugin { private WorldHandler worldHandler; private SmoothTeleport smoothTeleport; private AsyncManager asyncManager; + private WreckManager wreckManager; public static synchronized Movecraft getInstance() { return instance; @@ -189,8 +191,10 @@ public void onEnable() { asyncManager.runTaskTimer(this, 0, 1); MapUpdateManager.getInstance().runTaskTimer(this, 0, 1); + CraftManager.initialize(datapackInitialized); Bukkit.getScheduler().runTaskTimer(this, WorldManager.INSTANCE::run, 0,1); + wreckManager = new WreckManager(WorldManager.INSTANCE); getServer().getPluginManager().registerEvents(new InteractListener(), this); @@ -332,4 +336,8 @@ public SmoothTeleport getSmoothTeleport() { public AsyncManager getAsyncManager() { return asyncManager; } + + public @NotNull WreckManager getWreckManager(){ + return wreckManager; + } } diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java index fc2cfe889..7825b86e2 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java @@ -23,24 +23,26 @@ import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.async.rotation.RotationTask; import net.countercraft.movecraft.async.translation.TranslationTask; -import net.countercraft.movecraft.config.Settings; -import net.countercraft.movecraft.craft.*; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.craft.CraftManager; +import net.countercraft.movecraft.craft.PilotedCraft; +import net.countercraft.movecraft.craft.PlayerCraft; +import net.countercraft.movecraft.craft.SinkingCraft; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.events.CraftReleaseEvent; import net.countercraft.movecraft.mapUpdater.MapUpdateManager; -import net.countercraft.movecraft.mapUpdater.update.BlockCreateCommand; -import net.countercraft.movecraft.mapUpdater.update.UpdateCommand; -import net.countercraft.movecraft.util.hitboxes.HitBox; import net.kyori.adventure.text.Component; -import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -49,14 +51,8 @@ public class AsyncManager extends BukkitRunnable { private final Map ownershipMap = new HashMap<>(); private final BlockingQueue finishedAlgorithms = new LinkedBlockingQueue<>(); private final Set clearanceSet = new HashSet<>(); - private final Map wrecks = new HashMap<>(); - private final Map wreckWorlds = new HashMap<>(); - private final Map> wreckPhases = new HashMap<>(); - private final Map> processedFadeLocs = new HashMap<>(); private final Map cooldownCache = new WeakHashMap<>(); - private long lastFadeCheck = 0; - public AsyncManager() {} public void submitTask(AsyncTask task, Craft c) { @@ -71,15 +67,6 @@ public void submitCompletedTask(AsyncTask task) { finishedAlgorithms.add(task); } - public void addWreck(Craft craft){ - if(craft.getCollapsedHitBox().isEmpty() || Settings.FadeWrecksAfter == 0){ - return; - } - wrecks.put(craft.getCollapsedHitBox(), System.currentTimeMillis()); - wreckWorlds.put(craft.getCollapsedHitBox(), craft.getWorld()); - wreckPhases.put(craft.getCollapsedHitBox(), craft.getPhaseBlocks()); - } - private void processAlgorithmQueue() { int runLength = 10; int queueLength = finishedAlgorithms.size(); @@ -325,68 +312,11 @@ private void processSinking() { } } - private void processFadingBlocks() { - if (Settings.FadeWrecksAfter == 0) - return; - long ticksElapsed = (System.currentTimeMillis() - lastFadeCheck) / 50; - if (ticksElapsed <= Settings.FadeTickCooldown) - return; - - List processed = new ArrayList<>(); - for(Map.Entry entry : wrecks.entrySet()){ - if (Settings.FadeWrecksAfter * 1000L > System.currentTimeMillis() - entry.getValue()) - continue; - - final HitBox hitBox = entry.getKey(); - final Map phaseBlocks = wreckPhases.get(hitBox); - final World world = wreckWorlds.get(hitBox); - List commands = new ArrayList<>(); - int fadedBlocks = 0; - if (!processedFadeLocs.containsKey(world)) - processedFadeLocs.put(world, new HashSet<>()); - - int maxFadeBlocks = (int) (hitBox.size() * (Settings.FadePercentageOfWreckPerCycle / 100.0)); - //Iterate hitbox as a set to get more random locations - for (MovecraftLocation location : hitBox.asSet()){ - if (processedFadeLocs.get(world).contains(location)) - continue; - - if (fadedBlocks >= maxFadeBlocks) - break; - - final Location bLoc = location.toBukkit(world); - if ((Settings.FadeWrecksAfter - + Settings.ExtraFadeTimePerBlock.getOrDefault(bLoc.getBlock().getType(), 0)) - * 1000L > System.currentTimeMillis() - entry.getValue()) - continue; - - fadedBlocks++; - processedFadeLocs.get(world).add(location); - BlockData phaseBlock = phaseBlocks.getOrDefault(bLoc, Material.AIR.createBlockData()); - commands.add(new BlockCreateCommand(world, location, phaseBlock)); - } - MapUpdateManager.getInstance().scheduleUpdates(commands); - if (!processedFadeLocs.get(world).containsAll(hitBox.asSet())) - continue; - - processed.add(hitBox); - processedFadeLocs.get(world).removeAll(hitBox.asSet()); - } - for(HitBox hitBox : processed) { - wrecks.remove(hitBox); - wreckPhases.remove(hitBox); - wreckWorlds.remove(hitBox); - } - - lastFadeCheck = System.currentTimeMillis(); - } - public void run() { clearAll(); processCruise(); processSinking(); - processFadingBlocks(); processAlgorithmQueue(); // now cleanup craft that are bugged and have not moved in the past 60 seconds, diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/craft/CraftManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/craft/CraftManager.java index ec586acd2..1db3db008 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/craft/CraftManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/craft/CraftManager.java @@ -244,7 +244,7 @@ public void release(@NotNull Craft craft, @NotNull CraftReleaseEvent.Reason reas craft.getHitBox().getMinZ()) ); } - Movecraft.getInstance().getAsyncManager().addWreck(craft); + Movecraft.getInstance().getWreckManager().queueWreck(craft); } //region Craft management diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/FadeTask.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/FadeTask.java new file mode 100644 index 000000000..a8ae5f1b8 --- /dev/null +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/FadeTask.java @@ -0,0 +1,42 @@ +package net.countercraft.movecraft.features.fading; + +import net.countercraft.movecraft.MovecraftLocation; +import net.countercraft.movecraft.processing.MovecraftWorld; +import net.countercraft.movecraft.processing.WorldManager; +import net.countercraft.movecraft.processing.effects.Effect; +import net.countercraft.movecraft.processing.effects.SetBlockEffect; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.function.Supplier; + +/** + * Fades a block if the data for the intended block has not been mutated since creation. + */ +public class FadeTask implements Supplier { + private final @NotNull BlockData compareData; + private final @NotNull BlockData setData; + private final @NotNull MovecraftWorld world; + private final @NotNull MovecraftLocation location; + + public FadeTask(@NotNull BlockData compareData, @NotNull BlockData setData, @NotNull MovecraftWorld world, @NotNull MovecraftLocation location){ + this.compareData = compareData; + this.setData = setData; + this.world = world; + this.location = location; + } + + @Override + public Effect get() { + var testData = world.getData(location); + + return () -> Objects.requireNonNull(Bukkit.getWorld(world.getWorldUUID())) + .getChunkAtAsync(location.toBukkit(null)) + .thenRunAsync(() -> WorldManager.INSTANCE.submit(() -> testData.equals(compareData) + ? new SetBlockEffect(world, location, setData) + : null)); + } +} diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckManager.java new file mode 100644 index 000000000..ee12262df --- /dev/null +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckManager.java @@ -0,0 +1,45 @@ +package net.countercraft.movecraft.features.fading; + +import net.countercraft.movecraft.config.Settings; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.processing.WorldManager; +import net.countercraft.movecraft.util.MathUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Singleton for handling wreck disposal + */ +public class WreckManager { + private final @NotNull WorldManager worldManager; + + public WreckManager(@NotNull WorldManager worldManager){ + this.worldManager = Objects.requireNonNull(worldManager); + } + + /** + * Queue a wreck to be considered terminally destroyed, and hence appropriate for systems such as fading. + * + * @param craft the craft to handle as a wreck + */ + public void queueWreck(@NotNull Craft craft){ + if(craft.getCollapsedHitBox().isEmpty() || Settings.FadeWrecksAfter == 0){ + return; + } + + worldManager.submit(new WreckTask( + craft.getCollapsedHitBox(), + craft.getMovecraftWorld(), + craft + .getPhaseBlocks() + .entrySet() + .stream() + .collect(Collectors.toMap( + entry -> MathUtils.bukkit2MovecraftLoc(entry.getKey()), + Map.Entry::getValue + )))); + } +} diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckTask.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckTask.java new file mode 100644 index 000000000..5df495527 --- /dev/null +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckTask.java @@ -0,0 +1,73 @@ +package net.countercraft.movecraft.features.fading; + +import net.countercraft.movecraft.MovecraftLocation; +import net.countercraft.movecraft.config.Settings; +import net.countercraft.movecraft.processing.MovecraftWorld; +import net.countercraft.movecraft.processing.WorldManager; +import net.countercraft.movecraft.processing.effects.DeferredEffect; +import net.countercraft.movecraft.processing.effects.Effect; +import net.countercraft.movecraft.util.CollectorUtils; +import net.countercraft.movecraft.util.hitboxes.HitBox; +import org.bukkit.Material; +import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ForkJoinTask; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class WreckTask implements Supplier { + + private final @NotNull HitBox hitBox; + private final @NotNull Map phaseBlocks; + private final @NotNull MovecraftWorld world; + private final int fadeDelayTicks; + private final int maximumFadeDurationTicks; + + public WreckTask(@NotNull HitBox wreck, @NotNull MovecraftWorld world, @NotNull Map phaseBlocks){ + this.hitBox = Objects.requireNonNull(wreck); + this.phaseBlocks = Objects.requireNonNull(phaseBlocks); + this.world = Objects.requireNonNull(world); + this.fadeDelayTicks = Settings.FadeWrecksAfter * 20; + this.maximumFadeDurationTicks = (int) (Settings.FadeTickCooldown * (100.0 / Settings.FadePercentageOfWreckPerCycle)); + } + + @Override + public Effect get() { + var updates = hitBox + .asSet() + .stream() + .collect(Collectors.groupingBy(location -> location.scalarDivide(16).hadamardProduct(1,0,1), CollectorUtils.toHitBox())) + .values() + .stream() + .map(slice -> ForkJoinTask.adapt(() -> partialUpdate(slice))) + .toList(); + + return ForkJoinTask + .invokeAll(updates) + .stream() + .map(ForkJoinTask::join) + .reduce(Effect.NONE, Effect::andThen); + } + + private @NotNull Effect partialUpdate(@NotNull HitBox slice){ + Effect accumulator = Effect.NONE; + for (MovecraftLocation location : slice){ + // Get the existing data + final BlockData data = world.getData(location); + // Determine the replacement data + BlockData replacementData = phaseBlocks.getOrDefault(location, Material.AIR.createBlockData()); + // Calculate ticks until replacement + long fadeTicks = this.fadeDelayTicks; + fadeTicks += (int) (Math.random() * maximumFadeDurationTicks); + fadeTicks += 20L * Settings.ExtraFadeTimePerBlock.getOrDefault(data.getMaterial(), 0); + // Deffer replacement until time delay elapses + accumulator = accumulator.andThen(new DeferredEffect(fadeTicks, () -> WorldManager.INSTANCE.submit(new FadeTask(data, replacementData, world, location)))); + } + + // TODO: Determine if we need to reduce the spread of deferred effects due to runnable overhead + return accumulator; + } +} diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/DeferredEffect.java b/Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/DeferredEffect.java new file mode 100644 index 000000000..ee0864345 --- /dev/null +++ b/Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/DeferredEffect.java @@ -0,0 +1,36 @@ +package net.countercraft.movecraft.processing.effects; + +import net.countercraft.movecraft.Movecraft; +import net.countercraft.movecraft.processing.WorldManager; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * A wrapper effect that allows delaying the execution of a provided effect by a number of ticks. + */ +public class DeferredEffect implements Effect { + private final long delayTicks; + private final @NotNull Effect effect; + + public DeferredEffect(long delayTicks, @NotNull Effect effect){ + this.delayTicks = delayTicks; + this.effect = Objects.requireNonNull(effect); + } + + @Override + public void run() { + new BukkitRunnable(){ + @Override + public void run() { + WorldManager.INSTANCE.submit(() -> effect); + } + }.runTaskLaterAsynchronously(Movecraft.getInstance(), delayTicks); + } + + @Override + public boolean isAsync() { + return true; + } +} diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/SetBlockEffect.java b/Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/SetBlockEffect.java index 82aca582c..c31e93944 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/SetBlockEffect.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/SetBlockEffect.java @@ -1,16 +1,36 @@ package net.countercraft.movecraft.processing.effects; import net.countercraft.movecraft.MovecraftLocation; -import org.bukkit.Material; +import net.countercraft.movecraft.mapUpdater.update.BlockCreateCommand; +import net.countercraft.movecraft.processing.MovecraftWorld; +import org.bukkit.Bukkit; import org.bukkit.World; +import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.NotNull; +import java.util.Objects; + +/** + * Sets a block based on the provided data. + */ public final class SetBlockEffect implements Effect { - public SetBlockEffect(World world, MovecraftLocation location, Material material){ + private final @NotNull MovecraftWorld world; + private final @NotNull MovecraftLocation location; + private final @NotNull BlockData data; + public SetBlockEffect(@NotNull MovecraftWorld world, @NotNull MovecraftLocation location, @NotNull BlockData data){ + this.world = world; + this.location = location; + this.data = data; } @Override public void run() { + World bukkitWorld = Objects.requireNonNull( + Bukkit.getWorld(world.getWorldUUID()), + "Failed to access base World from MovecraftWorld"); + // TODO: Reverse indirection + new BlockCreateCommand(bukkitWorld, location, data).doUpdate(); } } diff --git a/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java b/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java index 1636dec68..1f3486f43 100644 --- a/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java +++ b/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java @@ -88,6 +88,26 @@ public MovecraftLocation subtract(MovecraftLocation l) { return new MovecraftLocation(getX() - l.getX(), getY() - l.getY(), getZ() - l.getZ()); } + public MovecraftLocation hadamardProduct(int x, int y, int z){ + return new MovecraftLocation(this.x*x, this.y*y, this.z*z); + } + + public MovecraftLocation hadamardProduct(MovecraftLocation location){ + return hadamardProduct(location.x, location.y, location.z); + } + + public MovecraftLocation scalarMultiply(int multiplier){ + return new MovecraftLocation(x * multiplier, y * multiplier, z * multiplier); + } + + public MovecraftLocation scalarDivide(int divisor){ + return new MovecraftLocation(x / divisor, y / divisor, z/divisor); + } + + public MovecraftLocation scalarMod(int modulus){ + return new MovecraftLocation(x % modulus, y & modulus, z % modulus); + } + /** * * Gives the euclidean distance between this MovecraftLocation and another MovecraftLocation diff --git a/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java b/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java index ecef2e3df..74378240d 100644 --- a/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java +++ b/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java @@ -4,6 +4,7 @@ import net.countercraft.movecraft.util.CompletableFutureTask; import org.bukkit.Bukkit; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -32,7 +33,7 @@ public String toString(){ }; private final ConcurrentLinkedQueue worldChanges = new ConcurrentLinkedQueue<>(); - private final ConcurrentLinkedQueue> tasks = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue> tasks = new ConcurrentLinkedQueue<>(); private final BlockingQueue currentTasks = new LinkedBlockingQueue<>(); private volatile boolean running = false; @@ -122,7 +123,7 @@ public void submit(Runnable task){ }); } - public void submit(Supplier task){ + public void submit(Supplier<@Nullable Effect> task){ tasks.add(task); } diff --git a/api/src/main/java/net/countercraft/movecraft/processing/effects/Effect.java b/api/src/main/java/net/countercraft/movecraft/processing/effects/Effect.java index 284ddbff4..f12d2f458 100644 --- a/api/src/main/java/net/countercraft/movecraft/processing/effects/Effect.java +++ b/api/src/main/java/net/countercraft/movecraft/processing/effects/Effect.java @@ -1,24 +1,79 @@ package net.countercraft.movecraft.processing.effects; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; + @FunctionalInterface public interface Effect { + /** + * A no-op effect for use in systems where a non-null effect is needed + */ + Effect NONE = new Effect() { + @Override + public void run() { + // No-op + } + + @Override + public boolean isAsync() { + return true; + } + + @Override + public @NotNull Effect andThen(@Nullable Effect chain){ + return chain == null ? this : chain; + } + }; + void run(); default boolean isAsync(){ return false; } - default @NotNull - Effect andThen(@Nullable Effect chain){ - if(chain == null){ + default @NotNull Effect andThen(@Nullable Effect chain){ + return new AndEffect(this, chain); + } + + class AndEffect implements Effect { + private final List effects = new ArrayList<>(); + + public AndEffect(Effect... effects){ + for (Effect effect : effects) { + andThen(effect); + } + } + + @Override + public void run() { + effects.forEach(Effect::run); + } + + @Override + public @NotNull Effect andThen(@Nullable Effect chain) { + if(this == chain){ + // copy if chaining to self to prevent concurrent modification + effects.addAll(effects.stream().toList()); + + return this; + } else if(chain instanceof AndEffect andChain){ + // Merge other AndChain instances + effects.addAll(andChain.effects); + + return this; + } else if(chain == NONE || chain == null){ + // Skip NONE + return this; + } + + // Otherwise add to current chain + effects.add(chain); + return this; } - return () -> { - this.run(); - chain.run(); - }; } } diff --git a/api/src/main/java/net/countercraft/movecraft/util/CollectorUtils.java b/api/src/main/java/net/countercraft/movecraft/util/CollectorUtils.java new file mode 100644 index 000000000..f98c4c4da --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/util/CollectorUtils.java @@ -0,0 +1,16 @@ +package net.countercraft.movecraft.util; + +import net.countercraft.movecraft.MovecraftLocation; +import net.countercraft.movecraft.util.hitboxes.BitmapHitBox; + +import java.util.stream.Collector; + +public class CollectorUtils { + /** + * Provides a collector for reducing streams of MovecraftLocations to HitBox instances + * @return A HitBox containing all collected MovecraftLocations + */ + public static Collector toHitBox(){ + return Collector.of(BitmapHitBox::new, BitmapHitBox::add, BitmapHitBox::union, t->t); + } +} diff --git a/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/IAsyncChunk.java b/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/IAsyncChunk.java index 72d98448c..2cf850445 100644 --- a/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/IAsyncChunk.java +++ b/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/IAsyncChunk.java @@ -7,6 +7,7 @@ import net.countercraft.movecraft.processing.WorldManager; import net.countercraft.movecraft.support.AsyncChunk; import net.minecraft.core.BlockPos; +import net.minecraft.world.level.chunk.ChunkAccess; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.block.BlockState; @@ -26,8 +27,11 @@ public BlockState load(@NotNull MovecraftLocation movecraftLocation) { } }); + private final ChunkAccess handle; + public IAsyncChunk(@NotNull Chunk chunk) { super(chunk); + handle = this.chunk.getHandle(); } @NotNull @@ -51,7 +55,7 @@ public Material getType(@NotNull MovecraftLocation location){ @Override @NotNull public BlockData getData(@NotNull MovecraftLocation location){ - return CraftBlockData.fromData(chunk.getHandle().getBlockState(new BlockPos(location.getX(), location.getY(), location.getZ()))); + return CraftBlockData.fromData(handle.getBlockState(new BlockPos(location.getX(), location.getY(), location.getZ()))); } }