From 290917ec0c638630ac3d0b0718f35ccfd4f0f0be Mon Sep 17 00:00:00 2001 From: Hazel <15641603+oh-noey@users.noreply.github.com> Date: Sun, 11 Aug 2024 07:48:32 -0700 Subject: [PATCH 01/17] Refine craft data tag system (#687) * Add annotations to data tag container * Composition over inheritance, concurrency * Concurrent registry * Avoid defaults on non-try/default methods * Field ordering, access modifiers * try method is not try * cleanup * Add javadoc * Remove defaults * Update return type * annotations * annotations * Discreet data tag registry * Fix build * Add iterator over entries --- .../net/countercraft/movecraft/Movecraft.java | 2 + .../movecraft/craft/BaseCraft.java | 29 +++++-- .../features/contacts/ContactsManager.java | 3 +- .../features/status/StatusManager.java | 3 +- .../countercraft/movecraft/craft/Craft.java | 31 ++------ .../craft/datatag/CraftDataTagContainer.java | 79 ++++++++++--------- .../craft/datatag/CraftDataTagRegistry.java | 54 +++++++++++++ 7 files changed, 133 insertions(+), 68 deletions(-) create mode 100644 api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagRegistry.java diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java index 2f298f1a0..d5f6eabbb 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java @@ -23,6 +23,7 @@ import net.countercraft.movecraft.config.Settings; import net.countercraft.movecraft.craft.ChunkManager; import net.countercraft.movecraft.craft.CraftManager; +import net.countercraft.movecraft.craft.datatag.CraftDataTagRegistry; import net.countercraft.movecraft.features.contacts.ContactsCommand; import net.countercraft.movecraft.features.contacts.ContactsManager; import net.countercraft.movecraft.features.contacts.ContactsSign; @@ -38,6 +39,7 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.FileOutputStream; diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/craft/BaseCraft.java b/Movecraft/src/main/java/net/countercraft/movecraft/craft/BaseCraft.java index e264fe2c3..2eec10dfd 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/craft/BaseCraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/craft/BaseCraft.java @@ -9,6 +9,8 @@ import net.countercraft.movecraft.async.translation.TranslationTask; import net.countercraft.movecraft.config.Settings; import net.countercraft.movecraft.craft.datatag.CraftDataTagContainer; +import net.countercraft.movecraft.craft.datatag.CraftDataTagKey; +import net.countercraft.movecraft.craft.datatag.CraftDataTagRegistry; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.localisation.I18nSupport; import net.countercraft.movecraft.processing.CachedMovecraftWorld; @@ -22,13 +24,23 @@ import net.countercraft.movecraft.util.hitboxes.SetHitBox; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; -import org.bukkit.*; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.block.data.BlockData; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; @@ -69,7 +81,8 @@ public abstract class BaseCraft implements Craft { private MovecraftLocation lastTranslation = new MovecraftLocation(0, 0, 0); private Map> trackedLocations = new HashMap<>(); - private final CraftDataTagContainer dataTagContainer = new CraftDataTagContainer(); + @NotNull + private final CraftDataTagContainer dataTagContainer; private final UUID uuid = UUID.randomUUID(); @@ -85,6 +98,7 @@ public BaseCraft(@NotNull CraftType type, @NotNull World world) { disabled = false; origPilotTime = System.currentTimeMillis(); audience = Audience.empty(); + dataTagContainer = new CraftDataTagContainer(); } @@ -536,8 +550,13 @@ public void setAudience(@NotNull Audience audience) { } @Override - public CraftDataTagContainer getDataTagContainer() { - return dataTagContainer; + public void setDataTag(final @NotNull CraftDataTagKey tagKey, final T data) { + dataTagContainer.set(tagKey, data); + } + + @Override + public T getDataTag(final @NotNull CraftDataTagKey tagKey) { + return dataTagContainer.get(this, tagKey); } @Override diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsManager.java index 359493f47..2647e6f00 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsManager.java @@ -4,6 +4,7 @@ import net.countercraft.movecraft.craft.*; import net.countercraft.movecraft.craft.datatag.CraftDataTagContainer; import net.countercraft.movecraft.craft.datatag.CraftDataTagKey; +import net.countercraft.movecraft.craft.datatag.CraftDataTagRegistry; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.events.*; import net.countercraft.movecraft.exception.EmptyHitBoxException; @@ -27,7 +28,7 @@ import java.util.*; public class ContactsManager extends BukkitRunnable implements Listener { - private static final CraftDataTagKey> RECENT_CONTACTS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "recent-contacts"), craft -> new WeakHashMap<>()); + private static final CraftDataTagKey> RECENT_CONTACTS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "recent-contacts"), craft -> new WeakHashMap<>()); @Override public void run() { diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java index 08b2b2afb..ed48e75d4 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java @@ -7,6 +7,7 @@ import net.countercraft.movecraft.craft.SinkingCraft; import net.countercraft.movecraft.craft.datatag.CraftDataTagContainer; import net.countercraft.movecraft.craft.datatag.CraftDataTagKey; +import net.countercraft.movecraft.craft.datatag.CraftDataTagRegistry; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.craft.type.RequiredBlockEntry; import net.countercraft.movecraft.features.status.events.CraftStatusUpdateEvent; @@ -33,7 +34,7 @@ import java.util.function.Supplier; public class StatusManager extends BukkitRunnable implements Listener { - private static final CraftDataTagKey LAST_STATUS_CHECK = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "last-status-check"), craft -> System.currentTimeMillis()); + private static final CraftDataTagKey LAST_STATUS_CHECK = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "last-status-check"), craft -> System.currentTimeMillis()); @Override public void run() { diff --git a/api/src/main/java/net/countercraft/movecraft/craft/Craft.java b/api/src/main/java/net/countercraft/movecraft/craft/Craft.java index 1e9e61555..2c13ad657 100644 --- a/api/src/main/java/net/countercraft/movecraft/craft/Craft.java +++ b/api/src/main/java/net/countercraft/movecraft/craft/Craft.java @@ -23,6 +23,7 @@ import net.countercraft.movecraft.TrackedLocation; import net.countercraft.movecraft.craft.datatag.CraftDataTagContainer; import net.countercraft.movecraft.craft.datatag.CraftDataTagKey; +import net.countercraft.movecraft.craft.datatag.CraftDataTagRegistry; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.processing.MovecraftWorld; import net.countercraft.movecraft.util.Counter; @@ -43,11 +44,11 @@ import java.util.*; public interface Craft { - CraftDataTagKey> CONTACTS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "contacts"), craft -> new ArrayList<>(0)); - CraftDataTagKey FUEL = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "fuel"), craft -> 0D); - CraftDataTagKey> MATERIALS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "materials"), craft -> new Counter<>()); - CraftDataTagKey NON_NEGLIGIBLE_BLOCKS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "non-negligible-blocks"), Craft::getOrigBlockCount); - CraftDataTagKey NON_NEGLIGIBLE_SOLID_BLOCKS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "non-negligible-solid-blocks"), Craft::getOrigBlockCount); + CraftDataTagKey> CONTACTS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "contacts"), craft -> new ArrayList<>(0)); + CraftDataTagKey FUEL = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "fuel"), craft -> 0D); + CraftDataTagKey> MATERIALS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "materials"), craft -> new Counter<>()); + CraftDataTagKey NON_NEGLIGIBLE_BLOCKS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "non-negligible-blocks"), Craft::getOrigBlockCount); + CraftDataTagKey NON_NEGLIGIBLE_SOLID_BLOCKS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "non-negligible-solid-blocks"), Craft::getOrigBlockCount); // Java disallows private or protected fields in interfaces, this is a workaround class Hidden { @@ -272,25 +273,9 @@ default void setLastDZ(int dZ){} void setAudience(Audience audience); - public default CraftDataTagContainer getDataTagContainer() { - return null; - } + void setDataTag(@NotNull final CraftDataTagKey tagKey, final T data); - public default boolean setDataTag(CraftDataTagKey tagKey, T data) { - CraftDataTagContainer container = this.getDataTagContainer(); - if (container == null) { - return false; - } - container.set(tagKey, data); - return true; - } - public default T getDataTag(CraftDataTagKey tagKey) { - CraftDataTagContainer container = this.getDataTagContainer(); - if (container == null) { - return null; - } - return container.get(this, tagKey); - } + T getDataTag(@NotNull final CraftDataTagKey tagKey); public default void markTileStateWithUUID(TileState tile) { // Add the marker diff --git a/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagContainer.java b/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagContainer.java index f4a9ad36a..c01ec6675 100644 --- a/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagContainer.java +++ b/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagContainer.java @@ -1,53 +1,56 @@ package net.countercraft.movecraft.craft.datatag; import net.countercraft.movecraft.craft.Craft; -import org.bukkit.NamespacedKey; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Supplier; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; -public class CraftDataTagContainer extends HashMap, Object> { +public class CraftDataTagContainer { + private final @NotNull ConcurrentMap<@NotNull CraftDataTagKey, @Nullable Object> backing; - public static final Map> REGISTERED_TAGS = new HashMap<>(); - - public static CraftDataTagKey tryRegisterTagKey(final NamespacedKey key, final Function supplier) throws IllegalArgumentException { - if (REGISTERED_TAGS.containsKey(key)) { - throw new IllegalArgumentException("Duplicate keys are not allowed!"); - } else { - CraftDataTagKey result = new CraftDataTagKey(key, supplier); - REGISTERED_TAGS.put(key, result); - return result; - } + public CraftDataTagContainer(){ + backing = new ConcurrentHashMap<>(); } - public T get(final Craft craft, CraftDataTagKey tagKey) { - if (!REGISTERED_TAGS.containsKey(tagKey.key)) { - // TODO: Log error - return null; + /** + * Gets the data value associated with the provided tagKey from a craft. + * + * @param craft the craft to perform a lookup against + * @param tagKey the tagKey to use for looking up the relevant data + * @return the tag value associate with the provided tagKey on the specified craft + * @param the value type of the registered data key + * @throws IllegalArgumentException when the provided tagKey is not registered + * @throws IllegalStateException when the provided tagKey does not match the underlying tag value + */ + public T get(final @NotNull Craft craft, @NotNull CraftDataTagKey tagKey) { + if (!CraftDataTagRegistry.INSTANCE.isRegistered(tagKey.key)) { + throw new IllegalArgumentException(String.format("The provided key %s was not registered.", tagKey)); } - T result = null; - if (!this.containsKey(tagKey)) { - result = tagKey.createNew(craft); - this.put(tagKey, result); - } else { - Object stored = this.getOrDefault(tagKey, tagKey.createNew(craft)); - try { - T temp = (T) stored; - result = temp; - } catch (ClassCastException cce) { - // TODO: Log error - result = tagKey.createNew(craft); - this.put(tagKey, result); - } + + Object stored = backing.computeIfAbsent(tagKey, ignored -> tagKey.createNew(craft)); + try { + //noinspection unchecked + return (T) stored; + } catch (ClassCastException cce) { + throw new IllegalStateException(String.format("The provided key %s has an invalid value type.", tagKey), cce); } - return result; } - public void set(CraftDataTagKey tagKey, @NotNull T value) { - this.put(tagKey, value); - } + /** + * Set the value associated with the provided tagKey on the associated craft. + * + * @param tagKey the tagKey to use for storing the relevant data + * @param value the value to set for future lookups + * @param the type of the value + */ + public void set(@NotNull CraftDataTagKey tagKey, @NotNull T value) { + if (!CraftDataTagRegistry.INSTANCE.isRegistered(tagKey.key)) { + throw new IllegalArgumentException(String.format("The provided key %s was not registered.", tagKey)); + } + backing.put(tagKey, value); + } } diff --git a/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagRegistry.java b/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagRegistry.java new file mode 100644 index 000000000..d542f8405 --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagRegistry.java @@ -0,0 +1,54 @@ +package net.countercraft.movecraft.craft.datatag; + +import net.countercraft.movecraft.craft.Craft; +import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.NotNull; + +import java.util.Enumeration; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Function; + +public class CraftDataTagRegistry { + public static final @NotNull CraftDataTagRegistry INSTANCE = new CraftDataTagRegistry(); + + private final @NotNull ConcurrentMap<@NotNull NamespacedKey, @NotNull CraftDataTagKey> _registeredTags; + + public CraftDataTagRegistry(){ + _registeredTags = new ConcurrentHashMap<>(); + } + + /** + * Registers a data tag to be attached to craft instances. The data tag will initialize to the value supplied by the + * initializer. Once a tag is registered, it can be accessed from crafts using the returned key through various + * methods. + * + * @param key the namespace key to use for registration, which must be unique + * @param initializer a default initializer for the value type + * @return A CraftDataTagKey, which can be used to control an associated value on a Craft instance + * @param the value type + * @throws IllegalArgumentException when the provided key is already registered + */ + public @NotNull CraftDataTagKey registerTagKey(final @NotNull NamespacedKey key, final @NotNull Function initializer) throws IllegalArgumentException { + CraftDataTagKey result = new CraftDataTagKey<>(key, initializer); + var previous = _registeredTags.putIfAbsent(key, result); + if(previous != null){ + throw new IllegalArgumentException(String.format("Key %s is already registered.", key)); + } + + return result; + } + + public boolean isRegistered(final @NotNull NamespacedKey key){ + return _registeredTags.containsKey(key); + } + + /** + * Get an iterable over all keys currently registered. + * @return An immutable iterable over the registry keys + */ + public @NotNull Iterable<@NotNull NamespacedKey> getAllKeys(){ + return _registeredTags.keySet().stream().toList(); + } +} From 9ccfe24bcbf1fc6890f3ea06a1b45507ecfe04cf Mon Sep 17 00:00:00 2001 From: Hazel <15641603+oh-noey@users.noreply.github.com> Date: Sun, 11 Aug 2024 07:48:45 -0700 Subject: [PATCH 02/17] Use processing for wrecks (#686) * 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 --- .../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 ++++ 12 files changed, 339 insertions(+), 93 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 945bbf771..7a3af6b93 100644 --- a/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java +++ b/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java @@ -87,6 +87,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); + } +} From 0c4f7af6a61affba7a24292f151805a675f90705 Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Sun, 11 Aug 2024 10:03:43 -0500 Subject: [PATCH 03/17] Bump version for maven publish (#689) * Bump version for maven publish * Publish to maven on tag push --- .github/workflows/gradle.yml | 2 +- buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index e20309259..4c055d332 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -35,7 +35,7 @@ jobs: run: ./gradlew clean shadowJar --parallel - name: Publish to GitHub Packages - if: ${{ github.event_name == 'release' }} + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') run: ./gradlew publish --parallel env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts index 91e1dbe0f..595a3d9cd 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts @@ -11,7 +11,7 @@ repositories { } group = "net.countercraft" -version = "8.0.0_beta-5_dev-1" +version = "8.0.0_beta-5_dev-2" tasks.withType() { options.encoding = "UTF-8" From e02019aaacf92d978da99a46a3df7f2fc4165e86 Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Sun, 11 Aug 2024 11:16:18 -0500 Subject: [PATCH 04/17] 1.21.1 (#691) --- Movecraft/build.gradle.kts | 2 +- v1_21/build.gradle.kts | 2 +- .../net/countercraft/movecraft/compat/v1_21/IWorldHandler.java | 2 +- .../countercraft/movecraft/support/v1_21/ISmoothTeleport.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Movecraft/build.gradle.kts b/Movecraft/build.gradle.kts index 7d261568d..683722e23 100644 --- a/Movecraft/build.gradle.kts +++ b/Movecraft/build.gradle.kts @@ -68,7 +68,7 @@ hangarPublish { platforms { register(io.papermc.hangarpublishplugin.model.Platforms.PAPER) { jar.set(tasks.shadowJar.flatMap { it.archiveFile }) - platformVersions.set(listOf("1.18.2", "1.20.6", "1.21")) + platformVersions.set(listOf("1.18.2", "1.20.6", "1.21.1")) } } } diff --git a/v1_21/build.gradle.kts b/v1_21/build.gradle.kts index 8d4c44fcd..d84125889 100644 --- a/v1_21/build.gradle.kts +++ b/v1_21/build.gradle.kts @@ -7,7 +7,7 @@ java.toolchain.languageVersion = JavaLanguageVersion.of(21) dependencies { api(project(":movecraft-api")) - paperweight.paperDevBundle("1.21-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT") } description = "Movecraft-v1_21" diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java index e2ed2fc0d..d7405bfdf 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java @@ -48,7 +48,7 @@ public class IWorldHandler extends WorldHandler { public IWorldHandler() { String version = Bukkit.getServer().getMinecraftVersion(); - if (!version.equals("1.21")) + if (!version.equals("1.21.1")) throw new IllegalStateException("Movecraft is not compatible with this version of Minecraft: " + version); } diff --git a/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java b/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java index f7bdfff99..0d77c7f9a 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java @@ -17,7 +17,7 @@ /** * Code derived from code taken with permission from MicleBrick * https://www.spigotmc.org/threads/teleport-player-smoothly.317416/ - * Used for 1.21 + * Used for 1.21.1 */ public class ISmoothTeleport extends SmoothTeleport { private final Field teleportPosField; From de8588bee33f02beeba692ddec61c748dd6a656a Mon Sep 17 00:00:00 2001 From: goodroach <68086172+goodroach@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:58:47 -1000 Subject: [PATCH 05/17] Collision explosion fix (#692) * add trackedlocations * reimplement TrackedLocation to specification * use built-in methods for vector manipulation * Fix merge * Fix build * Process tracked locations on all parent crafts * Update RotationTask.java * Add allowInternalCollisionExplosion feature * remove unused imports --------- Co-authored-by: TylerS1066 --- .../movecraft/async/translation/TranslationTask.java | 10 ++++++++++ .../countercraft/movecraft/craft/type/CraftType.java | 3 +++ 2 files changed, 13 insertions(+) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java b/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java index ed5112695..3e0aabef5 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java @@ -9,6 +9,7 @@ import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.craft.CraftManager; import net.countercraft.movecraft.craft.SinkingCraft; +import net.countercraft.movecraft.craft.SubCraft; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.events.CraftCollisionEvent; import net.countercraft.movecraft.events.CraftCollisionExplosionEvent; @@ -357,7 +358,16 @@ else if (world.equals(craft.getWorld()) } } else if ((craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION) > 0F) && System.currentTimeMillis() - craft.getOrigPilotTime() > craft.getType().getIntProperty(CraftType.EXPLOSION_ARMING_TIME)) { + Craft parentCraft = null; + if (craft instanceof SubCraft) { + parentCraft = ((SubCraft) craft).getParent(); + } for (MovecraftLocation location : collisionBox) { + if (parentCraft != null && parentCraft.getHitBox().contains(location) + && !parentCraft.getType().getBoolProperty(CraftType.ALLOW_INTERNAL_COLLISION_EXPLOSION)) { + //Prevents CollisionExplosion crafts from exploding inside the craft. + break; + } float explosionForce = craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION); boolean incendiary = craft.getType().getBoolProperty(CraftType.INCENDIARY_ON_CRASH); if (craft.getType().getBoolProperty(CraftType.FOCUSED_EXPLOSION)) { diff --git a/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java b/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java index 53c08d139..02644e8fd 100644 --- a/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java +++ b/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java @@ -190,6 +190,8 @@ final public class CraftType { public static final NamespacedKey CRUISE_ON_PILOT_LIFETIME = buildKey("cruise_on_pilot_lifetime"); public static final NamespacedKey EXPLOSION_ARMING_TIME = buildKey("explosion_arming_time"); + + public static final NamespacedKey ALLOW_INTERNAL_COLLISION_EXPLOSION = buildKey("allow_internal_collision_explosion"); //endregion @Contract("_ -> new") @@ -566,6 +568,7 @@ else if (o instanceof Integer) registerProperty(new BooleanProperty("mergePistonExtensions", MERGE_PISTON_EXTENSIONS, type -> false)); registerProperty(new IntegerProperty("cruiseOnPilotLifetime", CRUISE_ON_PILOT_LIFETIME, type -> 15*20)); registerProperty(new IntegerProperty("explosionArmingTime", EXPLOSION_ARMING_TIME, type -> 1000)); + registerProperty(new BooleanProperty("allowInternalCollisionExplosion", ALLOW_INTERNAL_COLLISION_EXPLOSION, type -> false)); /* Craft type transforms */ // Convert speed to TICK_COOLDOWN From b23f98a7c293dc3cab84c5fd03bc7f41204a8136 Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Thu, 15 Aug 2024 20:14:04 -0500 Subject: [PATCH 06/17] Underwater collision explosion (#693) * Remove ProtocolLib repo * Implement underwater collision explosion property --- .../movecraft/async/translation/TranslationTask.java | 12 +++++++----- .../countercraft/movecraft/craft/type/CraftType.java | 2 ++ .../kotlin/buildlogic.java-conventions.gradle.kts | 1 - 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java b/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java index 3e0aabef5..2de1ee195 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java @@ -368,15 +368,17 @@ else if (world.equals(craft.getWorld()) //Prevents CollisionExplosion crafts from exploding inside the craft. break; } - float explosionForce = craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION); + float explosionForce; + if (location.getY() < craft.getWaterLine()) { + explosionForce = craft.getType().getFloatProperty(CraftType.UNDERWATER_COLLISION_EXPLOSION); + } + else { + explosionForce = craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION); + } boolean incendiary = craft.getType().getBoolProperty(CraftType.INCENDIARY_ON_CRASH); if (craft.getType().getBoolProperty(CraftType.FOCUSED_EXPLOSION)) { explosionForce *= Math.min(oldHitBox.size(), craft.getType().getIntProperty(CraftType.MAX_SIZE)); } - //TODO: Account for underwater explosions - /*if (location.getY() < waterLine) { // underwater explosions require more force to do anything - explosionForce += 25;//TODO: find the correct amount - }*/ Location oldLocation = location.translate(-dx, -dy, -dz).toBukkit(craft.getWorld()); Location newLocation = location.toBukkit(world); if (!oldLocation.getBlock().getType().isAir()) { diff --git a/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java b/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java index 02644e8fd..98570e8e8 100644 --- a/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java +++ b/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java @@ -129,6 +129,7 @@ final public class CraftType { public static final NamespacedKey EXPLODE_ON_CRASH = buildKey("explode_on_crash"); public static final NamespacedKey INCENDIARY_ON_CRASH = buildKey("incendiary_on_crash"); public static final NamespacedKey COLLISION_EXPLOSION = buildKey("collision_explosion"); + public static final NamespacedKey UNDERWATER_COLLISION_EXPLOSION = buildKey("underwater_collision_explosion"); private static final NamespacedKey MIN_HEIGHT_LIMIT = buildKey("min_height_limit"); // Private key used as default for PER_WORLD_MIN_HEIGHT_LIMIT public static final NamespacedKey PER_WORLD_MIN_HEIGHT_LIMIT = buildKey("per_world_min_height_limit"); @@ -454,6 +455,7 @@ public static void registerTypeValidator(Predicate validator, String registerProperty(new FloatProperty("explodeOnCrash", EXPLODE_ON_CRASH, type -> 0F)); registerProperty(new BooleanProperty("incendiaryOnCrash", INCENDIARY_ON_CRASH, type -> false)); registerProperty(new FloatProperty("collisionExplosion", COLLISION_EXPLOSION, type -> 0F)); + registerProperty(new FloatProperty("underwaterCollisionExplosion", UNDERWATER_COLLISION_EXPLOSION, type -> type.getFloatProperty(COLLISION_EXPLOSION))); registerProperty(new IntegerProperty("minHeightLimit", MIN_HEIGHT_LIMIT, type -> Integer.MIN_VALUE)); registerProperty(new PerWorldProperty<>("perWorldMinHeightLimit", PER_WORLD_MIN_HEIGHT_LIMIT, (type, worldName) -> type.getIntProperty(MIN_HEIGHT_LIMIT))); diff --git a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts index 595a3d9cd..489d005a7 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts @@ -6,7 +6,6 @@ repositories { mavenLocal() maven("https://repo.maven.apache.org/maven2/") maven("https://oss.sonatype.org/content/repositories/snapshots/") - maven("https://repo.dmulloy2.net/nexus/repository/public/") maven("https://repo.papermc.io/repository/maven-public/") } From 953f7d0580bf64791fd07978df2413841a35c387 Mon Sep 17 00:00:00 2001 From: Vaan1310 <61906290+Intybyte@users.noreply.github.com> Date: Sat, 17 Aug 2024 19:03:30 +0200 Subject: [PATCH 07/17] Feature/direction dependent block attachment (#681) * Attempt 1 * Attempt 5 * Solved! (incomeplete) Needs a block whitelist ty <3 ohnoey * Add Lanterns + small refactor * Remove commented code * Refactor shorthanded variables * Remove Debugging and useless variable * Undo: Revert a conditional * Made SupportUtils class * Adding Reminders + More robust FaceAttachable checks * Add directionDependentMaterials * Fix Signs (only get wall signs as they are directional) * Use `Tag.WALL_SIGNS` instead * Created Tags.WALL_TORCHES and used that instead --------- Co-authored-by: TylerS1066 --- .../tasks/detection/DetectionTask.java | 50 +++++++++++++------ .../tasks/detection/SupportUtils.java | 47 +++++++++++++++++ .../movecraft/MovecraftLocation.java | 5 ++ .../movecraft/craft/type/CraftType.java | 14 ++++-- .../net/countercraft/movecraft/util/Tags.java | 4 +- 5 files changed, 98 insertions(+), 22 deletions(-) create mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/processing/tasks/detection/SupportUtils.java diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/processing/tasks/detection/DetectionTask.java b/Movecraft/src/main/java/net/countercraft/movecraft/processing/tasks/detection/DetectionTask.java index 30efd8031..1cf1eff8c 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/processing/tasks/detection/DetectionTask.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/processing/tasks/detection/DetectionTask.java @@ -39,6 +39,8 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -48,9 +50,11 @@ import java.util.Collections; import java.util.Comparator; import java.util.Deque; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -304,7 +308,6 @@ private void frontier() { ConcurrentLinkedQueue nextFrontier = new ConcurrentLinkedQueue<>(); currentFrontier.add(startLocation); currentFrontier.addAll(Arrays.stream(SHIFTS).map(startLocation::add).collect(Collectors.toList())); - visited.addAll(currentFrontier); int threads = Runtime.getRuntime().availableProcessors(); while(!currentFrontier.isEmpty() && size.intValue() < type.getIntProperty(CraftType.MAX_SIZE) + threads) { List> tasks = new ArrayList<>(); @@ -330,6 +333,14 @@ public String toString(){ private class DetectAction implements Runnable { private final ConcurrentLinkedQueue currentFrontier; private final ConcurrentLinkedQueue nextFrontier; + private static DetectionPredicate chain; + + static { + chain = FORBIDDEN_BLOCK_VALIDATOR; + for(var validator : VALIDATORS) { + chain = chain.and(validator); + } + } private DetectAction(ConcurrentLinkedQueue currentFrontier, ConcurrentLinkedQueue nextFrontier) { this.currentFrontier = currentFrontier; @@ -339,31 +350,42 @@ private DetectAction(ConcurrentLinkedQueue currentFrontier, C @Override public void run() { MovecraftLocation probe; + EnumSet directionalDependent = type.getMaterialSetProperty(CraftType.DIRECTIONAL_DEPENDENT_MATERIALS); + while((probe = currentFrontier.poll()) != null) { - visitedMaterials.computeIfAbsent(movecraftWorld.getMaterial(probe), Functions.forSupplier(ConcurrentLinkedDeque::new)).add(probe); + BlockData blockData = movecraftWorld.getData(probe); + Material material = blockData.getMaterial(); + + Optional blockDataOptional = SupportUtils.getSupportFace(blockData, directionalDependent); + if (blockDataOptional.isPresent()) { + BlockFace facing = blockDataOptional.get(); + MovecraftLocation relativeLoc = probe.getRelative(facing); + + if (!legal.contains(relativeLoc)) + continue; + } + + if(!visited.add(probe)) + continue; + + visitedMaterials.computeIfAbsent(material, Functions.forSupplier(ConcurrentLinkedDeque::new)).add(probe); if(!ALLOWED_BLOCK_VALIDATOR.validate(probe, type, movecraftWorld, player).isSucess()) continue; - DetectionPredicate chain = FORBIDDEN_BLOCK_VALIDATOR; - for(var validator : VALIDATORS) { - chain = chain.and(validator); - } var result = chain.validate(probe, type, movecraftWorld, player); - if(result.isSucess()) { + if (result.isSucess()) { legal.add(probe); - if(Tags.FLUID.contains(movecraftWorld.getMaterial(probe))) + if (Tags.FLUID.contains(material)) fluid.add(probe); size.increment(); - materials.computeIfAbsent(movecraftWorld.getMaterial(probe), Functions.forSupplier(ConcurrentLinkedDeque::new)).add(probe); - for(MovecraftLocation shift : SHIFTS) { + materials.computeIfAbsent(material, Functions.forSupplier(ConcurrentLinkedDeque::new)).add(probe); + for (MovecraftLocation shift : SHIFTS) { var shifted = probe.add(shift); - if(visited.add(shifted)) - nextFrontier.add(shifted); + nextFrontier.add(shifted); } - } - else { + } else { illegal.add(probe); audience.sendMessage(Component.text(result.getMessage())); } diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/processing/tasks/detection/SupportUtils.java b/Movecraft/src/main/java/net/countercraft/movecraft/processing/tasks/detection/SupportUtils.java new file mode 100644 index 000000000..af2decf2e --- /dev/null +++ b/Movecraft/src/main/java/net/countercraft/movecraft/processing/tasks/detection/SupportUtils.java @@ -0,0 +1,47 @@ +package net.countercraft.movecraft.processing.tasks.detection; + +import org.bukkit.Material; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Directional; +import org.bukkit.block.data.FaceAttachable; +import org.bukkit.block.data.type.Lantern; + +import java.util.EnumSet; +import java.util.Optional; + +/** + * @author Intybyte/Vaan1310 + * An util craft that uses block data to get its supporting block + */ + +public class SupportUtils { + public static Optional getSupportFace(BlockData data, EnumSet directionalDependent) { + + Material material = data.getMaterial(); + if (!directionalDependent.contains(material)) { + return Optional.empty(); + } + + //TODO: Use pattern matched switch statements once we update do Java 21 + //TODO: This should become Hangable instead when we drop support for 1.18 + if (data instanceof Lantern lantern) + return Optional.of(lantern.isHanging() ? BlockFace.UP : BlockFace.DOWN); + + if (data instanceof Directional directional) { + BlockFace normalCase = directional.getFacing().getOppositeFace(); + + if (data instanceof FaceAttachable faceAttachable) { + return switch (faceAttachable.getAttachedFace()) { + case FLOOR -> Optional.of(BlockFace.DOWN); + case WALL -> Optional.of(normalCase); + case CEILING -> Optional.of(BlockFace.UP); + }; + } + + return Optional.of(normalCase); + } + + return Optional.empty(); + } +} diff --git a/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java b/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java index 7a3af6b93..1f3486f43 100644 --- a/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java +++ b/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java @@ -20,6 +20,7 @@ import com.google.common.primitives.UnsignedInteger; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.block.BlockFace; import org.jetbrains.annotations.NotNull; import static net.countercraft.movecraft.util.BitMath.mask; @@ -174,4 +175,8 @@ public int compareTo(@NotNull MovecraftLocation other) { } return 0; } + + public MovecraftLocation getRelative(BlockFace facing) { + return this.translate(facing.getModX(), facing.getModY(), facing.getModZ()); + } } diff --git a/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java b/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java index 98570e8e8..eb01c1f41 100644 --- a/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java +++ b/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java @@ -43,10 +43,7 @@ import net.countercraft.movecraft.util.Tags; import net.kyori.adventure.key.Key; import net.kyori.adventure.sound.Sound; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.World; +import org.bukkit.*; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -191,7 +188,7 @@ final public class CraftType { public static final NamespacedKey CRUISE_ON_PILOT_LIFETIME = buildKey("cruise_on_pilot_lifetime"); public static final NamespacedKey EXPLOSION_ARMING_TIME = buildKey("explosion_arming_time"); - + public static final NamespacedKey DIRECTIONAL_DEPENDENT_MATERIALS = buildKey("directional_dependent_materials"); public static final NamespacedKey ALLOW_INTERNAL_COLLISION_EXPLOSION = buildKey("allow_internal_collision_explosion"); //endregion @@ -395,6 +392,13 @@ public static void registerTypeValidator(Predicate validator, String /* Optional properties */ registerProperty(new RequiredBlockProperty("flyblocks", FLY_BLOCKS, type -> new HashSet<>())); registerProperty(new RequiredBlockProperty("detectionblocks", DETECTION_BLOCKS, type -> new HashSet<>())); + registerProperty(new MaterialSetProperty("directionDependentMaterials", DIRECTIONAL_DEPENDENT_MATERIALS, type -> { + var set = EnumSet.of(Material.LADDER, Material.LEVER, Material.GRINDSTONE, Material.LANTERN); + set.addAll(Tag.WALL_SIGNS.getValues()); + set.addAll(Tags.WALL_TORCHES); + return set; + })); + registerProperty(new ObjectPropertyImpl("forbiddenSignStrings", FORBIDDEN_SIGN_STRINGS, (data, type, fileKey, namespacedKey) -> data.getStringListOrEmpty(fileKey).stream().map( String::toLowerCase).collect(Collectors.toSet()), diff --git a/api/src/main/java/net/countercraft/movecraft/util/Tags.java b/api/src/main/java/net/countercraft/movecraft/util/Tags.java index 6e8a03257..ec91b5384 100644 --- a/api/src/main/java/net/countercraft/movecraft/util/Tags.java +++ b/api/src/main/java/net/countercraft/movecraft/util/Tags.java @@ -4,14 +4,11 @@ import org.bukkit.Keyed; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.Registry; import org.bukkit.Tag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.EnumSet; -import java.util.HashSet; -import java.util.Set; public class Tags { public static final EnumSet WATER = EnumSet.of(Material.WATER, Material.BUBBLE_COLUMN); @@ -22,6 +19,7 @@ public class Tags { public static final EnumSet FRAGILE_MATERIALS = EnumSet.noneOf(Material.class); public static final EnumSet FALL_THROUGH_BLOCKS = EnumSet.noneOf(Material.class); public static final EnumSet BUCKETS = EnumSet.of(Material.LAVA_BUCKET, Material.WATER_BUCKET, Material.MILK_BUCKET, Material.COD_BUCKET, Material.PUFFERFISH_BUCKET, Material.SALMON_BUCKET, Material.TROPICAL_FISH_BUCKET); + public static final EnumSet WALL_TORCHES = EnumSet.of(Material.WALL_TORCH, Material.SOUL_WALL_TORCH, Material.REDSTONE_WALL_TORCH); static { FRAGILE_MATERIALS.add(Material.PISTON_HEAD); From dc0566119bb548411f28e308644a8e7a4060beb4 Mon Sep 17 00:00:00 2001 From: Vaan1310 <61906290+Intybyte@users.noreply.github.com> Date: Sat, 17 Aug 2024 19:39:16 +0200 Subject: [PATCH 08/17] Rewrite/rotation task (#683) * New methods in CruiseDirection * Change RotationTask execute to use new methods * Early return + rename variables * Extract Rotate function * Extract Rotate Human Entity * checkChests refactor * Extract rotation message * Correction Entity List * Fix rotateEntitiesOnCraft * Merge clockwise and anticlockwise rotation * Refactor `fromBlockFace` method --- .../async/rotation/RotationTask.java | 296 +++++++++--------- .../movecraft/CruiseDirection.java | 49 ++- 2 files changed, 176 insertions(+), 169 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/async/rotation/RotationTask.java b/Movecraft/src/main/java/net/countercraft/movecraft/async/rotation/RotationTask.java index 9e9453f11..327e9e86c 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/async/rotation/RotationTask.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/async/rotation/RotationTask.java @@ -48,12 +48,11 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.HumanEntity; -import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.InventoryView; import java.util.HashSet; -import java.util.Iterator; +import java.util.List; import java.util.Set; import static net.countercraft.movecraft.util.MathUtils.withinWorldBorder; @@ -195,150 +194,142 @@ protected void execute() { tOP.setX(tOP.getBlockX() + 0.5); tOP.setZ(tOP.getBlockZ() + 0.5); - if (!(craft instanceof SinkingCraft && craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS)) - && craft.getType().getBoolProperty(CraftType.MOVE_ENTITIES)) { - Location midpoint = new Location( - craft.getWorld(), - (oldHitBox.getMaxX() + oldHitBox.getMinX())/2.0, - (oldHitBox.getMaxY() + oldHitBox.getMinY())/2.0, - (oldHitBox.getMaxZ() + oldHitBox.getMinZ())/2.0); - for(Entity entity : craft.getWorld().getNearbyEntities(midpoint, - oldHitBox.getXLength() / 2.0 + 1, - oldHitBox.getYLength() / 2.0 + 2, - oldHitBox.getZLength() / 2.0 + 1)) { - - if (entity instanceof HumanEntity) { - InventoryView inventoryView = ((HumanEntity) entity).getOpenInventory(); - if (inventoryView.getType() != InventoryType.CRAFTING) { - Location l = Movecraft.getInstance().getWorldHandler().getAccessLocation(inventoryView); - if (l != null) { - MovecraftLocation location = new MovecraftLocation(l.getBlockX(), l.getBlockY(), l.getBlockZ()); - if (oldHitBox.contains(location)) { - location = MathUtils.rotateVec(rotation, location.subtract(originPoint)).add(originPoint); - updates.add(new AccessLocationUpdateCommand(inventoryView, location.toBukkit(w))); - } - } - } - } + rotateEntitiesOnCraft(tOP); - if (!craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS) || ( - (entity.getType() == EntityType.PLAYER || entity.getType() == EntityType.PRIMED_TNT) - && !(craft instanceof SinkingCraft) - )) { - // Player is onboard this craft - - Location adjustedPLoc = entity.getLocation().subtract(tOP); - - double[] rotatedCoords = MathUtils.rotateVecNoRound(rotation, - adjustedPLoc.getX(), adjustedPLoc.getZ()); - float newYaw = rotation == MovecraftRotation.CLOCKWISE ? 90F : -90F; - - CraftTeleportEntityEvent e = new CraftTeleportEntityEvent(craft, entity); - Bukkit.getServer().getPluginManager().callEvent(e); - if (e.isCancelled()) - continue; - - EntityUpdateCommand eUp = new EntityUpdateCommand(entity, - rotatedCoords[0] + tOP.getX() - entity.getLocation().getX(), - 0, - rotatedCoords[1] + tOP.getZ() - entity.getLocation().getZ(), - newYaw, - 0 - ); - updates.add(eUp); - } - } + Craft craft1 = getCraft(); + if (craft1.getCruising()) { + CruiseDirection direction = craft1.getCruiseDirection(); + craft1.setCruiseDirection(direction.getRotated(rotation)); } - if (getCraft().getCruising()) { - if (rotation == MovecraftRotation.ANTICLOCKWISE) { - // ship faces west - switch (getCraft().getCruiseDirection()) { - case WEST: - getCraft().setCruiseDirection(CruiseDirection.SOUTH); - break; - // ship faces east - case EAST: - getCraft().setCruiseDirection(CruiseDirection.NORTH); - break; - // ship faces north - case SOUTH: - getCraft().setCruiseDirection(CruiseDirection.EAST); - break; - // ship faces south - case NORTH: - getCraft().setCruiseDirection(CruiseDirection.WEST); - break; - } - } else if (rotation == MovecraftRotation.CLOCKWISE) { - // ship faces west - switch (getCraft().getCruiseDirection()) { - case WEST: - getCraft().setCruiseDirection(CruiseDirection.NORTH); - break; - // ship faces east - case EAST: - getCraft().setCruiseDirection(CruiseDirection.SOUTH); - break; - // ship faces north - case SOUTH: - getCraft().setCruiseDirection(CruiseDirection.WEST); - break; - // ship faces south - case NORTH: - getCraft().setCruiseDirection(CruiseDirection.EAST); - break; - } - } + // if you rotated a subcraft, update the parent with the new blocks + if (!this.isSubCraft) { + return; } + // also find the furthest extent from center and notify the player of the new direction + int farthestX = 0; + int farthestZ = 0; + for (MovecraftLocation loc : newHitBox) { + if (Math.abs(loc.getX() - originPoint.getX()) > Math.abs(farthestX)) + farthestX = loc.getX() - originPoint.getX(); + if (Math.abs(loc.getZ() - originPoint.getZ()) > Math.abs(farthestZ)) + farthestZ = loc.getZ() - originPoint.getZ(); + } + Component faceMessage = I18nSupport.getInternationalisedComponent("Rotation - Farthest Extent Facing") + .append(Component.text(" ")); - // if you rotated a subcraft, update the parent with the new blocks - if (this.isSubCraft) { - // also find the furthest extent from center and notify the player of the new direction - int farthestX = 0; - int farthestZ = 0; - for (MovecraftLocation loc : newHitBox) { - if (Math.abs(loc.getX() - originPoint.getX()) > Math.abs(farthestX)) - farthestX = loc.getX() - originPoint.getX(); - if (Math.abs(loc.getZ() - originPoint.getZ()) > Math.abs(farthestZ)) - farthestZ = loc.getZ() - originPoint.getZ(); + faceMessage = faceMessage.append(getRotationMessage(farthestX, farthestZ)); + craft1.getAudience().sendMessage(faceMessage); + + craftsInWorld = CraftManager.getInstance().getCraftsInWorld(craft1.getWorld()); + for (Craft craft : craftsInWorld) { + if (newHitBox.intersection(craft.getHitBox()).isEmpty() || craft == craft1) { + continue; } - Component faceMessage = I18nSupport.getInternationalisedComponent("Rotation - Farthest Extent Facing") - .append(Component.text(" ")); - if (Math.abs(farthestX) > Math.abs(farthestZ)) { - if (farthestX > 0) { - faceMessage = faceMessage.append(I18nSupport.getInternationalisedComponent("Contact/Subcraft Rotate - East")); - } else { - faceMessage = faceMessage.append(I18nSupport.getInternationalisedComponent("Contact/Subcraft Rotate - West")); - } + //newHitBox.addAll(CollectionUtils.filter(craft.getHitBox(),newHitBox)); + //craft.setHitBox(newHitBox); + if (Settings.Debug) { + Bukkit.broadcastMessage(String.format("Size of %s hitbox: %d, Size of %s hitbox: %d", this.craft.getType().getStringProperty(CraftType.NAME), newHitBox.size(), craft.getType().getStringProperty(CraftType.NAME), craft.getHitBox().size())); + } + craft.setHitBox(craft.getHitBox().difference(oldHitBox).union(newHitBox)); + if (Settings.Debug){ + Bukkit.broadcastMessage(String.format("Hitbox of craft %s intersects hitbox of craft %s", this.craft.getType().getStringProperty(CraftType.NAME), craft.getType().getStringProperty(CraftType.NAME))); + Bukkit.broadcastMessage(String.format("Size of %s hitbox: %d, Size of %s hitbox: %d", this.craft.getType().getStringProperty(CraftType.NAME), newHitBox.size(), craft.getType().getStringProperty(CraftType.NAME), craft.getHitBox().size())); + } + break; + } + + } + + private Component getRotationMessage(int farthestX, int farthestZ) { + if (Math.abs(farthestX) > Math.abs(farthestZ)) { + if (farthestX > 0) { + return I18nSupport.getInternationalisedComponent("Contact/Subcraft Rotate - East"); } else { - if (farthestZ > 0) { - faceMessage = faceMessage.append(I18nSupport.getInternationalisedComponent("Contact/Subcraft Rotate - South")); - } else { - faceMessage = faceMessage.append(I18nSupport.getInternationalisedComponent("Contact/Subcraft Rotate - North")); - } + return I18nSupport.getInternationalisedComponent("Contact/Subcraft Rotate - West"); } - getCraft().getAudience().sendMessage(faceMessage); - - craftsInWorld = CraftManager.getInstance().getCraftsInWorld(getCraft().getWorld()); - for (Craft craft : craftsInWorld) { - if (!newHitBox.intersection(craft.getHitBox()).isEmpty() && craft != getCraft()) { - //newHitBox.addAll(CollectionUtils.filter(craft.getHitBox(),newHitBox)); - //craft.setHitBox(newHitBox); - if (Settings.Debug) { - Bukkit.broadcastMessage(String.format("Size of %s hitbox: %d, Size of %s hitbox: %d", this.craft.getType().getStringProperty(CraftType.NAME), newHitBox.size(), craft.getType().getStringProperty(CraftType.NAME), craft.getHitBox().size())); - } - craft.setHitBox(craft.getHitBox().difference(oldHitBox).union(newHitBox)); - if (Settings.Debug){ - Bukkit.broadcastMessage(String.format("Hitbox of craft %s intersects hitbox of craft %s", this.craft.getType().getStringProperty(CraftType.NAME), craft.getType().getStringProperty(CraftType.NAME))); - Bukkit.broadcastMessage(String.format("Size of %s hitbox: %d, Size of %s hitbox: %d", this.craft.getType().getStringProperty(CraftType.NAME), newHitBox.size(), craft.getType().getStringProperty(CraftType.NAME), craft.getHitBox().size())); - } - break; - } + } else { + if (farthestZ > 0) { + return I18nSupport.getInternationalisedComponent("Contact/Subcraft Rotate - South"); + } else { + return I18nSupport.getInternationalisedComponent("Contact/Subcraft Rotate - North"); } } + } + private void rotateEntitiesOnCraft(Location tOP) { + if (!craft.getType().getBoolProperty(CraftType.MOVE_ENTITIES) + || (craft instanceof SinkingCraft + && craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS))) { + return; + } + + Location midpoint = new Location( + craft.getWorld(), + (oldHitBox.getMaxX() + oldHitBox.getMinX())/2.0, + (oldHitBox.getMaxY() + oldHitBox.getMinY())/2.0, + (oldHitBox.getMaxZ() + oldHitBox.getMinZ())/2.0); + + List entityList = List.of(EntityType.PLAYER, EntityType.PRIMED_TNT); + for(Entity entity : craft.getWorld().getNearbyEntities(midpoint, + oldHitBox.getXLength() / 2.0 + 1, + oldHitBox.getYLength() / 2.0 + 2, + oldHitBox.getZLength() / 2.0 + 1)) { + + rotateHumanEntity(entity); + + if (craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS) + && (!entityList.contains(entity.getType()) + || craft instanceof SinkingCraft)) { + continue; + }// Player is onboard this craft + Location adjustedPLoc = entity.getLocation().subtract(tOP); + + double[] rotatedCoords = MathUtils.rotateVecNoRound(rotation, + adjustedPLoc.getX(), adjustedPLoc.getZ()); + float newYaw = rotation == MovecraftRotation.CLOCKWISE ? 90F : -90F; + + CraftTeleportEntityEvent e = new CraftTeleportEntityEvent(craft, entity); + Bukkit.getServer().getPluginManager().callEvent(e); + if (e.isCancelled()) + continue; + + EntityUpdateCommand eUp = new EntityUpdateCommand(entity, + rotatedCoords[0] + tOP.getX() - entity.getLocation().getX(), + 0, + rotatedCoords[1] + tOP.getZ() - entity.getLocation().getZ(), + newYaw, + 0 + ); + updates.add(eUp); + + + } + } + + private void rotateHumanEntity(Entity entity) { + if (!(entity instanceof HumanEntity)) { + return; + } + + InventoryView inventoryView = ((HumanEntity) entity).getOpenInventory(); + if (inventoryView.getType() == InventoryType.CRAFTING) { + return; + } + + Location l = Movecraft.getInstance().getWorldHandler().getAccessLocation(inventoryView); + if (l == null) { + return; + } + + MovecraftLocation location = new MovecraftLocation(l.getBlockX(), l.getBlockY(), l.getBlockZ()); + if (!oldHitBox.contains(location)) { + return; + } + + location = MathUtils.rotateVec(rotation, location.subtract(originPoint)).add(originPoint); + updates.add(new AccessLocationUpdateCommand(inventoryView, location.toBukkit(w))); } public MovecraftLocation getOriginPoint() { @@ -368,37 +359,32 @@ public boolean getIsSubCraft() { private boolean checkChests(Material mBlock, MovecraftLocation newLoc) { Material testMaterial; MovecraftLocation aroundNewLoc; + final World world = craft.getWorld(); aroundNewLoc = newLoc.translate(1, 0, 0); - testMaterial = craft.getWorld().getBlockAt(aroundNewLoc.getX(), aroundNewLoc.getY(), aroundNewLoc.getZ()).getType(); - if (testMaterial.equals(mBlock)) { - if (!oldHitBox.contains(aroundNewLoc)) { - return false; - } - } + testMaterial = world.getBlockAt(aroundNewLoc.getX(), aroundNewLoc.getY(), aroundNewLoc.getZ()).getType(); + if (checkOldHitBox(testMaterial, mBlock, aroundNewLoc)) + return false; aroundNewLoc = newLoc.translate(-1, 0, 0); - testMaterial = craft.getWorld().getBlockAt(aroundNewLoc.getX(), aroundNewLoc.getY(), aroundNewLoc.getZ()).getType(); - if (testMaterial.equals(mBlock)) { - if (!oldHitBox.contains(aroundNewLoc)) { - return false; - } - } + testMaterial = world.getBlockAt(aroundNewLoc.getX(), aroundNewLoc.getY(), aroundNewLoc.getZ()).getType(); + if (checkOldHitBox(testMaterial, mBlock, aroundNewLoc)) + return false; aroundNewLoc = newLoc.translate(0, 0, 1); - testMaterial = craft.getWorld().getBlockAt(aroundNewLoc.getX(), aroundNewLoc.getY(), aroundNewLoc.getZ()).getType(); - if (testMaterial.equals(mBlock)) { - if (!oldHitBox.contains(aroundNewLoc)) { - return false; - } - } + testMaterial = world.getBlockAt(aroundNewLoc.getX(), aroundNewLoc.getY(), aroundNewLoc.getZ()).getType(); + + if (checkOldHitBox(testMaterial, mBlock, aroundNewLoc)) + return false; aroundNewLoc = newLoc.translate(0, 0, -1); - testMaterial = craft.getWorld().getBlockAt(aroundNewLoc.getX(), aroundNewLoc.getY(), aroundNewLoc.getZ()).getType(); - return !testMaterial.equals(mBlock) || oldHitBox.contains(aroundNewLoc); + testMaterial = world.getBlockAt(aroundNewLoc.getX(), aroundNewLoc.getY(), aroundNewLoc.getZ()).getType(); + return !checkOldHitBox(testMaterial, mBlock, aroundNewLoc); } - + private boolean checkOldHitBox(Material testMaterial, Material mBlock, MovecraftLocation aroundNewLoc) { + return testMaterial.equals(mBlock) && !oldHitBox.contains(aroundNewLoc); + } public MutableHitBox getNewHitBox() { return newHitBox; diff --git a/api/src/main/java/net/countercraft/movecraft/CruiseDirection.java b/api/src/main/java/net/countercraft/movecraft/CruiseDirection.java index 251ae2daa..b569903a3 100644 --- a/api/src/main/java/net/countercraft/movecraft/CruiseDirection.java +++ b/api/src/main/java/net/countercraft/movecraft/CruiseDirection.java @@ -38,20 +38,41 @@ else if(rawDirection == (byte) 0x43) } public static CruiseDirection fromBlockFace(BlockFace direction) { - if(direction.getOppositeFace() == BlockFace.NORTH) - return NORTH; - else if(direction.getOppositeFace() == BlockFace.SOUTH) - return SOUTH; - else if(direction.getOppositeFace() == BlockFace.EAST) - return EAST; - else if(direction.getOppositeFace() == BlockFace.WEST) - return WEST; - else if(direction.getOppositeFace() == BlockFace.UP) - return UP; - else if(direction.getOppositeFace() == BlockFace.DOWN) - return DOWN; - else - return NONE; + return switch (direction.getOppositeFace()) { + case NORTH -> NORTH; + case SOUTH -> SOUTH; + case EAST -> EAST; + case WEST -> WEST; + case UP -> UP; + case DOWN -> DOWN; + default -> NONE; + }; + } + + public CruiseDirection getOpposite() { + return switch (this) { + case NORTH -> SOUTH; + case SOUTH -> NORTH; + case EAST -> WEST; + case WEST -> EAST; + case UP -> DOWN; + case DOWN -> UP; + case NONE -> NONE; + }; + } + + public CruiseDirection getRotated(MovecraftRotation rotation) { + return switch(rotation) { + case CLOCKWISE -> switch (this) { + case NORTH -> EAST; + case SOUTH -> WEST; + case EAST -> SOUTH; + case WEST -> NORTH; + default -> this; + }; + case ANTICLOCKWISE -> getRotated(MovecraftRotation.CLOCKWISE).getOpposite(); + case NONE -> this; + }; } } From a3bef3076901b3fd677b38b390c4afa256bc9292 Mon Sep 17 00:00:00 2001 From: Vaan1310 <61906290+Intybyte@users.noreply.github.com> Date: Mon, 19 Aug 2024 23:30:44 +0200 Subject: [PATCH 09/17] Add LANTERNS tag (#695) --- .../java/net/countercraft/movecraft/craft/type/CraftType.java | 3 ++- api/src/main/java/net/countercraft/movecraft/util/Tags.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java b/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java index eb01c1f41..d0f66b287 100644 --- a/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java +++ b/api/src/main/java/net/countercraft/movecraft/craft/type/CraftType.java @@ -393,9 +393,10 @@ public static void registerTypeValidator(Predicate validator, String registerProperty(new RequiredBlockProperty("flyblocks", FLY_BLOCKS, type -> new HashSet<>())); registerProperty(new RequiredBlockProperty("detectionblocks", DETECTION_BLOCKS, type -> new HashSet<>())); registerProperty(new MaterialSetProperty("directionDependentMaterials", DIRECTIONAL_DEPENDENT_MATERIALS, type -> { - var set = EnumSet.of(Material.LADDER, Material.LEVER, Material.GRINDSTONE, Material.LANTERN); + var set = EnumSet.of(Material.LADDER, Material.LEVER, Material.GRINDSTONE); set.addAll(Tag.WALL_SIGNS.getValues()); set.addAll(Tags.WALL_TORCHES); + set.addAll(Tags.LANTERNS); return set; })); diff --git a/api/src/main/java/net/countercraft/movecraft/util/Tags.java b/api/src/main/java/net/countercraft/movecraft/util/Tags.java index ec91b5384..399ebb92a 100644 --- a/api/src/main/java/net/countercraft/movecraft/util/Tags.java +++ b/api/src/main/java/net/countercraft/movecraft/util/Tags.java @@ -20,6 +20,7 @@ public class Tags { public static final EnumSet FALL_THROUGH_BLOCKS = EnumSet.noneOf(Material.class); public static final EnumSet BUCKETS = EnumSet.of(Material.LAVA_BUCKET, Material.WATER_BUCKET, Material.MILK_BUCKET, Material.COD_BUCKET, Material.PUFFERFISH_BUCKET, Material.SALMON_BUCKET, Material.TROPICAL_FISH_BUCKET); public static final EnumSet WALL_TORCHES = EnumSet.of(Material.WALL_TORCH, Material.SOUL_WALL_TORCH, Material.REDSTONE_WALL_TORCH); + public static final EnumSet LANTERNS = EnumSet.of(Material.LANTERN, Material.SOUL_LANTERN); static { FRAGILE_MATERIALS.add(Material.PISTON_HEAD); From 2296ae4ecefad1e47cc445a14b00f5e3aa5f33ae Mon Sep 17 00:00:00 2001 From: Nicole <52045376+drfiveminusmint@users.noreply.github.com> Date: Mon, 19 Aug 2024 17:54:51 -0400 Subject: [PATCH 10/17] 1.20/1.21 moving block ticks (#694) * 1.20 Moving Block Ticks Implements Block and Tile Tick movement on 1.20 * 1.21 implementation The same code should hopefully work for 1.21 --- .../movecraft/compat/v1_18/IWorldHandler.java | 1 + .../movecraft/compat/v1_20/IWorldHandler.java | 95 +++++++++++++------ .../compat/v1_20/NextTickProvider.java | 22 ++--- .../movecraft/compat/v1_21/IWorldHandler.java | 86 +++++++++++------ .../compat/v1_21/NextTickProvider.java | 25 ++--- 5 files changed, 142 insertions(+), 87 deletions(-) diff --git a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/IWorldHandler.java b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/IWorldHandler.java index 4db751f85..5babf0fe9 100644 --- a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/IWorldHandler.java +++ b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/IWorldHandler.java @@ -70,6 +70,7 @@ public void rotateCraft(@NotNull Craft craft, @NotNull MovecraftLocation originP ServerLevel nativeWorld = ((CraftWorld) craft.getWorld()).getHandle(); List tiles = new ArrayList<>(); List ticks = new ArrayList<>(); + //get the tiles for (BlockPos position : rotatedPositions.keySet()) { diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java index 019dd0ba8..676ea054f 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java @@ -16,9 +16,11 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.ticks.LevelChunkTicks; import net.minecraft.world.ticks.ScheduledTick; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -31,7 +33,12 @@ import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; @SuppressWarnings("unused") public class IWorldHandler extends WorldHandler { @@ -67,15 +74,20 @@ public void rotateCraft(@NotNull Craft craft, @NotNull MovecraftLocation originP //******************************************* ServerLevel nativeWorld = ((CraftWorld) craft.getWorld()).getHandle(); List tiles = new ArrayList<>(); + List ticks = new ArrayList<>(); //get the tiles for (BlockPos position : rotatedPositions.keySet()) { - //BlockEntity tile = nativeWorld.removeBlockEntity(position); BlockEntity tile = removeBlockEntity(nativeWorld, position); - if (tile == null) - continue; -// tile.a(ROTATION[rotation.ordinal()]); + if (tile != null) + tiles.add(new TileHolder(tile, position)); + //get the nextTick to move with the tile - tiles.add(new TileHolder(tile, tickProvider.getNextTick(nativeWorld, position), position)); + ScheduledTick tickHere = tickProvider.getNextTick(nativeWorld, position); + if (tickHere != null) { + ((LevelChunkTicks) nativeWorld.getChunkAt(position).getBlockTicks()).removeIf( + (Predicate) scheduledTick -> scheduledTick.equals(tickHere)); + ticks.add(new TickHolder(tickHere, position)); + } } //******************************************* @@ -100,12 +112,16 @@ public void rotateCraft(@NotNull Craft craft, @NotNull MovecraftLocation originP //* Step four: replace all the tiles * //******************************************* //TODO: go by chunks - for (TileHolder tileHolder : tiles) { + for (TileHolder tileHolder : tiles) moveBlockEntity(nativeWorld, rotatedPositions.get(tileHolder.getTilePosition()), tileHolder.getTile()); - if (tileHolder.getNextTick() == null) - continue; + for (TickHolder tickHolder : ticks) { final long currentTime = nativeWorld.serverLevelData.getGameTime(); - nativeWorld.getBlockTicks().schedule(new ScheduledTick<>((Block) tileHolder.getNextTick().type(), rotatedPositions.get(tileHolder.getNextTick().pos()), tileHolder.getNextTick().triggerTick() - currentTime, tileHolder.getNextTick().priority(), tileHolder.getNextTick().subTickOrder())); + nativeWorld.getBlockTicks().schedule(new ScheduledTick<>( + (Block) tickHolder.getTick().type(), + rotatedPositions.get(tickHolder.getTick().pos()), + tickHolder.getTick().triggerTick() - currentTime, + tickHolder.getTick().priority(), + tickHolder.getTick().subTickOrder())); } //******************************************* @@ -134,20 +150,24 @@ public void translateCraft(@NotNull Craft craft, @NotNull MovecraftLocation disp //* Step two: Get the tiles * //******************************************* List tiles = new ArrayList<>(); + List ticks = new ArrayList<>(); //get the tiles for (int i = 0, positionsSize = positions.size(); i < positionsSize; i++) { BlockPos position = positions.get(i); if (oldNativeWorld.getBlockState(position) == Blocks.AIR.defaultBlockState()) continue; - //BlockEntity tile = nativeWorld.removeBlockEntity(position); + BlockEntity tile = removeBlockEntity(oldNativeWorld, position); - if (tile == null) - continue; - //get the nextTick to move with the tile + if (tile != null) + tiles.add(new TileHolder(tile,position)); - //nativeWorld.capturedTileEntities.remove(position); - //nativeWorld.getChunkAtWorldCoords(position).getTileEntities().remove(position); - tiles.add(new TileHolder(tile, tickProvider.getNextTick(oldNativeWorld, position), position)); + //get the nextTick to move with the tile + ScheduledTick tickHere = tickProvider.getNextTick(nativeWorld, position); + if (tickHere != null) { + ((LevelChunkTicks) nativeWorld.getChunkAt(position).getBlockTicks()).removeIf( + (Predicate) scheduledTick -> scheduledTick.equals(tickHere)); + ticks.add(new TickHolder(tickHere, position)); + } } //******************************************* @@ -173,13 +193,11 @@ public void translateCraft(@NotNull Craft craft, @NotNull MovecraftLocation disp //* Step four: replace all the tiles * //******************************************* //TODO: go by chunks - for (int i = 0, tilesSize = tiles.size(); i < tilesSize; i++) { - TileHolder tileHolder = tiles.get(i); + for (TileHolder tileHolder : tiles) moveBlockEntity(nativeWorld, tileHolder.getTilePosition().offset(translateVector), tileHolder.getTile()); - if (tileHolder.getNextTick() == null) - continue; + for (TickHolder tickHolder : ticks) { final long currentTime = nativeWorld.getGameTime(); - nativeWorld.getBlockTicks().schedule(new ScheduledTick<>((Block) tileHolder.getNextTick().type(), tileHolder.getTilePosition().offset(translateVector), tileHolder.getNextTick().triggerTick() - currentTime, tileHolder.getNextTick().priority(), tileHolder.getNextTick().subTickOrder())); + nativeWorld.getBlockTicks().schedule(new ScheduledTick<>((Block) tickHolder.getTick().type(), tickHolder.getTickPosition().offset(translateVector), tickHolder.getTick().triggerTick() - currentTime, tickHolder.getTick().priority(), tickHolder.getTick().subTickOrder())); } //******************************************* //* Step five: Destroy the leftovers * @@ -291,14 +309,11 @@ private void moveBlockEntity(@NotNull Level nativeWorld, @NotNull BlockPos newPo private static class TileHolder { @NotNull private final BlockEntity tile; - @Nullable - private final ScheduledTick nextTick; @NotNull private final BlockPos tilePosition; - public TileHolder(@NotNull BlockEntity tile, @Nullable ScheduledTick nextTick, @NotNull BlockPos tilePosition) { + public TileHolder(@NotNull BlockEntity tile, @NotNull BlockPos tilePosition) { this.tile = tile; - this.nextTick = nextTick; this.tilePosition = tilePosition; } @@ -308,14 +323,32 @@ public BlockEntity getTile() { return tile; } - @Nullable - public ScheduledTick getNextTick() { - return nextTick; - } - @NotNull public BlockPos getTilePosition() { return tilePosition; } } + + private static class TickHolder { + @NotNull + private final ScheduledTick tick; + @NotNull + private final BlockPos tickPosition; + + public TickHolder(@NotNull ScheduledTick tick, @NotNull BlockPos tilePosition) { + this.tick = tick; + this.tickPosition = tilePosition; + } + + + @NotNull + public ScheduledTick getTick() { + return tick; + } + + @NotNull + public BlockPos getTickPosition() { + return tickPosition; + } + } } \ No newline at end of file diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/NextTickProvider.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/NextTickProvider.java index bceb388fc..1513e5ef4 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/NextTickProvider.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/NextTickProvider.java @@ -4,6 +4,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.ticks.LevelChunkTicks; import net.minecraft.world.ticks.LevelTicks; import net.minecraft.world.ticks.ScheduledTick; import org.jetbrains.annotations.NotNull; @@ -12,31 +13,28 @@ import java.lang.reflect.Field; import java.util.List; import java.util.Queue; +import java.util.stream.Stream; public class NextTickProvider { @Nullable public ScheduledTick getNextTick(@NotNull ServerLevel world, @NotNull BlockPos position){ - LevelTicks tickList = world.getBlockTicks(); + LevelChunkTicks tickList = (LevelChunkTicks) world + .getChunk(position) + .getBlockTicks(); + var box = BoundingBox.encapsulatingPositions(List.of(position)); if(box.isEmpty()){ return null; } - Queue> toRunThisTick; - try { - Field toRunThisTickField = LevelTicks.class.getDeclaredField("g"); // g is obfuscated toRunThisTick - toRunThisTickField.setAccessible(true); - toRunThisTick = (Queue>) toRunThisTickField.get(tickList); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - return null; - } - for (var iter = toRunThisTick.iterator(); iter.hasNext(); ) { + + Stream> ticks = tickList.getAll(); + + for (var iter = ticks.iterator(); iter.hasNext(); ) { var next = iter.next(); if (!next.pos().equals(position)) { continue; } - iter.remove(); return next; } return null; diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java index d7405bfdf..4a48783c8 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java @@ -19,6 +19,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.ticks.LevelChunkTicks; import net.minecraft.world.ticks.ScheduledTick; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -32,6 +33,7 @@ import java.lang.reflect.Field; import java.util.*; +import java.util.function.Predicate; @SuppressWarnings("unused") public class IWorldHandler extends WorldHandler { @@ -67,15 +69,21 @@ public void rotateCraft(@NotNull Craft craft, @NotNull MovecraftLocation originP //******************************************* ServerLevel nativeWorld = ((CraftWorld) craft.getWorld()).getHandle(); List tiles = new ArrayList<>(); + List ticks = new ArrayList<>(); //get the tiles for (BlockPos position : rotatedPositions.keySet()) { //BlockEntity tile = nativeWorld.removeBlockEntity(position); BlockEntity tile = removeBlockEntity(nativeWorld, position); - if (tile == null) - continue; -// tile.a(ROTATION[rotation.ordinal()]); + if (tile != null) + tiles.add(new TileHolder(tile, position)); + //get the nextTick to move with the tile - tiles.add(new TileHolder(tile, tickProvider.getNextTick(nativeWorld, position), position)); + ScheduledTick tickHere = tickProvider.getNextTick(nativeWorld, position); + if (tickHere != null) { + ((LevelChunkTicks) nativeWorld.getChunkAt(position).getBlockTicks()).removeIf( + (Predicate) scheduledTick -> scheduledTick.equals(tickHere)); + ticks.add(new TickHolder(tickHere, position)); + } } //******************************************* @@ -100,12 +108,16 @@ public void rotateCraft(@NotNull Craft craft, @NotNull MovecraftLocation originP //* Step four: replace all the tiles * //******************************************* //TODO: go by chunks - for (TileHolder tileHolder : tiles) { + for (TileHolder tileHolder : tiles) moveBlockEntity(nativeWorld, rotatedPositions.get(tileHolder.getTilePosition()), tileHolder.getTile()); - if (tileHolder.getNextTick() == null) - continue; + for (TickHolder tickHolder : ticks) { final long currentTime = nativeWorld.serverLevelData.getGameTime(); - nativeWorld.getBlockTicks().schedule(new ScheduledTick<>((Block) tileHolder.getNextTick().type(), rotatedPositions.get(tileHolder.getNextTick().pos()), tileHolder.getNextTick().triggerTick() - currentTime, tileHolder.getNextTick().priority(), tileHolder.getNextTick().subTickOrder())); + nativeWorld.getBlockTicks().schedule(new ScheduledTick<>( + (Block) tickHolder.getTick().type(), + rotatedPositions.get(tickHolder.getTick().pos()), + tickHolder.getTick().triggerTick() - currentTime, + tickHolder.getTick().priority(), + tickHolder.getTick().subTickOrder())); } //******************************************* @@ -134,6 +146,7 @@ public void translateCraft(@NotNull Craft craft, @NotNull MovecraftLocation disp //* Step two: Get the tiles * //******************************************* List tiles = new ArrayList<>(); + List ticks = new ArrayList<>(); //get the tiles for (int i = 0, positionsSize = positions.size(); i < positionsSize; i++) { BlockPos position = positions.get(i); @@ -141,14 +154,16 @@ public void translateCraft(@NotNull Craft craft, @NotNull MovecraftLocation disp continue; //BlockEntity tile = nativeWorld.removeBlockEntity(position); BlockEntity tile = removeBlockEntity(oldNativeWorld, position); - if (tile == null) - continue; - //get the nextTick to move with the tile - - //nativeWorld.capturedTileEntities.remove(position); - //nativeWorld.getChunkAtWorldCoords(position).getTileEntities().remove(position); - tiles.add(new TileHolder(tile, tickProvider.getNextTick(oldNativeWorld, position), position)); + if (tile != null) + tiles.add(new TileHolder(tile,position)); + //get the nextTick to move with the tile + ScheduledTick tickHere = tickProvider.getNextTick(nativeWorld, position); + if (tickHere != null) { + ((LevelChunkTicks) nativeWorld.getChunkAt(position).getBlockTicks()).removeIf( + (Predicate) scheduledTick -> scheduledTick.equals(tickHere)); + ticks.add(new TickHolder(tickHere, position)); + } } //******************************************* //* Step three: Translate all the blocks * @@ -173,13 +188,11 @@ public void translateCraft(@NotNull Craft craft, @NotNull MovecraftLocation disp //* Step four: replace all the tiles * //******************************************* //TODO: go by chunks - for (int i = 0, tilesSize = tiles.size(); i < tilesSize; i++) { - TileHolder tileHolder = tiles.get(i); + for (TileHolder tileHolder : tiles) moveBlockEntity(nativeWorld, tileHolder.getTilePosition().offset(translateVector), tileHolder.getTile()); - if (tileHolder.getNextTick() == null) - continue; + for (TickHolder tickHolder : ticks) { final long currentTime = nativeWorld.getGameTime(); - nativeWorld.getBlockTicks().schedule(new ScheduledTick<>((Block) tileHolder.getNextTick().type(), tileHolder.getTilePosition().offset(translateVector), tileHolder.getNextTick().triggerTick() - currentTime, tileHolder.getNextTick().priority(), tileHolder.getNextTick().subTickOrder())); + nativeWorld.getBlockTicks().schedule(new ScheduledTick<>((Block) tickHolder.getTick().type(), tickHolder.getTickPosition().offset(translateVector), tickHolder.getTick().triggerTick() - currentTime, tickHolder.getTick().priority(), tickHolder.getTick().subTickOrder())); } //******************************************* //* Step five: Destroy the leftovers * @@ -291,14 +304,11 @@ private void moveBlockEntity(@NotNull Level nativeWorld, @NotNull BlockPos newPo private static class TileHolder { @NotNull private final BlockEntity tile; - @Nullable - private final ScheduledTick nextTick; @NotNull private final BlockPos tilePosition; - public TileHolder(@NotNull BlockEntity tile, @Nullable ScheduledTick nextTick, @NotNull BlockPos tilePosition) { + public TileHolder(@NotNull BlockEntity tile, @NotNull BlockPos tilePosition) { this.tile = tile; - this.nextTick = nextTick; this.tilePosition = tilePosition; } @@ -308,14 +318,32 @@ public BlockEntity getTile() { return tile; } - @Nullable - public ScheduledTick getNextTick() { - return nextTick; - } - @NotNull public BlockPos getTilePosition() { return tilePosition; } } + + private static class TickHolder { + @NotNull + private final ScheduledTick tick; + @NotNull + private final BlockPos tickPosition; + + public TickHolder(@NotNull ScheduledTick tick, @NotNull BlockPos tilePosition) { + this.tick = tick; + this.tickPosition = tilePosition; + } + + + @NotNull + public ScheduledTick getTick() { + return tick; + } + + @NotNull + public BlockPos getTickPosition() { + return tickPosition; + } + } } \ No newline at end of file diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/NextTickProvider.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/NextTickProvider.java index c29d9ddce..b5898f390 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/NextTickProvider.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/NextTickProvider.java @@ -4,39 +4,34 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.ticks.LevelTicks; +import net.minecraft.world.ticks.LevelChunkTicks; import net.minecraft.world.ticks.ScheduledTick; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.lang.reflect.Field; import java.util.List; -import java.util.Queue; +import java.util.stream.Stream; public class NextTickProvider { @Nullable public ScheduledTick getNextTick(@NotNull ServerLevel world, @NotNull BlockPos position){ - LevelTicks tickList = world.getBlockTicks(); + LevelChunkTicks tickList = (LevelChunkTicks) world + .getChunk(position) + .getBlockTicks(); + var box = BoundingBox.encapsulatingPositions(List.of(position)); if(box.isEmpty()){ return null; } - Queue> toRunThisTick; - try { - Field toRunThisTickField = LevelTicks.class.getDeclaredField("g"); // g is obfuscated toRunThisTick - toRunThisTickField.setAccessible(true); - toRunThisTick = (Queue>) toRunThisTickField.get(tickList); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - return null; - } - for (var iter = toRunThisTick.iterator(); iter.hasNext(); ) { + + Stream> ticks = tickList.getAll(); + + for (var iter = ticks.iterator(); iter.hasNext(); ) { var next = iter.next(); if (!next.pos().equals(position)) { continue; } - iter.remove(); return next; } return null; From 5246f5f0ba4aa02fb87fff67035dca0e168f9771 Mon Sep 17 00:00:00 2001 From: Vaan1310 <61906290+Intybyte@users.noreply.github.com> Date: Wed, 21 Aug 2024 00:52:44 +0200 Subject: [PATCH 11/17] Rewrite/translation task (#684) * Remove useless if statements * Extract Process all nether portals * Extract Process High Craft * Extract Process Gravity * Extract preventsTorpedoRocketsPilots * Extract processHumanEntity * Small changes * Fix WorldHeight check + Small refactor * Fix Check * 4 cycle * Incendiary Fix + Remove unused imports * Fix processHighCraft * Use minHeight instead --- .../async/translation/TranslationTask.java | 337 +++++++++++------- 1 file changed, 200 insertions(+), 137 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java b/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java index 2de1ee195..d1d23459f 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/async/translation/TranslationTask.java @@ -5,11 +5,7 @@ import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.async.AsyncTask; import net.countercraft.movecraft.config.Settings; -import net.countercraft.movecraft.craft.ChunkManager; -import net.countercraft.movecraft.craft.Craft; -import net.countercraft.movecraft.craft.CraftManager; -import net.countercraft.movecraft.craft.SinkingCraft; -import net.countercraft.movecraft.craft.SubCraft; +import net.countercraft.movecraft.craft.*; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.events.CraftCollisionEvent; import net.countercraft.movecraft.events.CraftCollisionExplosionEvent; @@ -110,45 +106,18 @@ protected void execute() throws InterruptedException, ExecutionException { fail(preTranslateEvent.getFailMessage(), preTranslateEvent.isPlayingFailSound()); return; } - if (dx != preTranslateEvent.getDx()) - dx = preTranslateEvent.getDx(); - if (dy != preTranslateEvent.getDy()) - dy = preTranslateEvent.getDy(); - if (dz != preTranslateEvent.getDz()) - dz = preTranslateEvent.getDz(); + + dx = preTranslateEvent.getDx(); + dy = preTranslateEvent.getDy(); + dz = preTranslateEvent.getDz(); + world = preTranslateEvent.getWorld(); final int minY = oldHitBox.getMinY(); final int maxY = oldHitBox.getMaxY(); // proccess nether portals - if (Settings.CraftsUseNetherPortals && craft.getWorld().getEnvironment() != Environment.THE_END - && world.equals(craft.getWorld())) { - - // ensure chunks are loaded for portal checking only if change in location is - // large - Set chunksToLoad = ChunkManager.getChunks(oldHitBox, world, dx, dy, dz); - MovecraftChunk.addSurroundingChunks(chunksToLoad, 2); - ChunkManager.checkChunks(chunksToLoad); - if (!chunksToLoad.isEmpty()) - ChunkManager.syncLoadChunks(chunksToLoad).get(); - - for (MovecraftLocation oldLocation : oldHitBox) { - - Location location = oldLocation.translate(dx, dy, dz).toBukkit(craft.getWorld()); - Block block = craft.getWorld().getBlockAt(location); - if (block.getType() == Material.NETHER_PORTAL) { - if (processNetherPortal(block)) { - sound = Sound.BLOCK_PORTAL_TRAVEL; - volume = 0.25f; - break; - } - - } - - } - - } + processAllNetherPortals(); // ensure chunks are loaded only if world is different or change in location is // large @@ -161,52 +130,11 @@ protected void execute() throws InterruptedException, ExecutionException { // Only modify dy when not switching worlds //Check if the craft is too high - if (world.equals(craft.getWorld()) - && (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_LIMIT, craft.getWorld()) - < craft.getHitBox().getMinY()) - dy = Math.min(dy, -1); - else if (world.equals(craft.getWorld()) - && (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_ABOVE_GROUND, - craft.getWorld()) > 0) { - final MovecraftLocation middle = oldHitBox.getMidPoint(); - int testY = minY; - while (testY > 0) { - testY--; - if (!craft.getWorld().getBlockAt(middle.getX(), testY, middle.getZ()).getType().isAir()) - break; - } - if (maxY - testY - > (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_ABOVE_GROUND, world)) - dy = Math.min(dy, -1); - } - //Process gravity - if (world.equals(craft.getWorld()) && craft.getType().getBoolProperty(CraftType.USE_GRAVITY) - && !(craft instanceof SinkingCraft)) { - int incline = inclineCraft(oldHitBox); - if (incline > 0) { - boolean tooSteep = craft.getType().getIntProperty(CraftType.GRAVITY_INCLINE_DISTANCE) > -1 - && incline > craft.getType().getIntProperty(CraftType.GRAVITY_INCLINE_DISTANCE); - if (tooSteep && craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION) <= 0F) { - fail(I18nSupport.getInternationalisedString("Translation - Failed Incline too steep")); - return; - } - dy = tooSteep ? 0 : incline; - } else if (!isOnGround(oldHitBox) && craft.getType().getBoolProperty(CraftType.CAN_HOVER)) { - MovecraftLocation midPoint = oldHitBox.getMidPoint(); - int centreMinY = oldHitBox.getMinYAt(midPoint.getX(), midPoint.getZ()); - int groundY = centreMinY; - World w = craft.getWorld(); - while (groundY - 1 >= w.getMinHeight() - && (w.getBlockAt(midPoint.getX(), groundY - 1, midPoint.getZ()).getType().isAir() - || craft.getType().getMaterialSetProperty(CraftType.PASSTHROUGH_BLOCKS).contains( - w.getBlockAt(midPoint.getX(), groundY - 1, midPoint.getZ()).getType()))) { - groundY--; - } - if (centreMinY - groundY > craft.getType().getIntProperty(CraftType.HOVER_LIMIT)) - dy = -1; - } else if (!isOnGround(oldHitBox)) - dy = dropDistance(oldHitBox); - } + processHighCraft(minY, maxY); + //Process gravity (must be same world too) + if (processGravity()) + return; + //Fail the movement if the craft is too high and if the craft is not explosive int maxHeightLimit = (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_LIMIT, world); int minHeightLimit = (int) craft.getType().getPerWorldProperty(CraftType.PER_WORLD_MIN_HEIGHT_LIMIT, world); @@ -333,17 +261,15 @@ else if (world.equals(craft.getWorld()) } newHitBox.removeAll(air); for (MovecraftLocation location : collisionBox) { - CraftType type = craft.getType(); - - if (type.getFloatProperty(CraftType.EXPLODE_ON_CRASH) > 0F) { + if (craft.getType().getFloatProperty(CraftType.EXPLODE_ON_CRASH) > 0F) { if (System.currentTimeMillis() - craft.getOrigPilotTime() <= 1000) { continue; } Location loc = location.toBukkit(craft.getWorld()); if (!loc.getBlock().getType().isAir() && ThreadLocalRandom.current().nextDouble(1) < .05) { updates.add(new ExplosionUpdateCommand(loc, - type.getFloatProperty(CraftType.EXPLODE_ON_CRASH), - type.getBoolProperty(CraftType.INCENDIARY_ON_CRASH))); + craft.getType().getFloatProperty(CraftType.EXPLODE_ON_CRASH), + craft.getType().getBoolProperty(CraftType.INCENDIARY_ON_CRASH))); collisionExplosion = true; } } @@ -414,60 +340,197 @@ else if (world.equals(craft.getWorld()) updates.add(new CraftTranslateCommand(craft, new MovecraftLocation(dx, dy, dz), world)); //prevents torpedo and rocket pilots - if (!(craft instanceof SinkingCraft && craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS)) - && craft.getType().getBoolProperty(CraftType.MOVE_ENTITIES)) { - Location midpoint = new Location( - craft.getWorld(), - (oldHitBox.getMaxX() + oldHitBox.getMinX()) / 2.0, - (oldHitBox.getMaxY() + oldHitBox.getMinY()) / 2.0, - (oldHitBox.getMaxZ() + oldHitBox.getMinZ()) / 2.0); - for (Entity entity : craft.getWorld().getNearbyEntities(midpoint, - oldHitBox.getXLength() / 2.0 + 1, - oldHitBox.getYLength() / 2.0 + 2, - oldHitBox.getZLength() / 2.0 + 1 - )) { - - if (entity instanceof HumanEntity) { - InventoryView inventoryView = ((HumanEntity) entity).getOpenInventory(); - if (inventoryView.getType() != InventoryType.CRAFTING) { - Location l = Movecraft.getInstance().getWorldHandler().getAccessLocation(inventoryView); - if (l != null) { - MovecraftLocation location = new MovecraftLocation(l.getBlockX(), l.getBlockY(), l.getBlockZ()); - if (oldHitBox.contains(location)) { - location = location.translate(dx, dy, dz); - updates.add(new AccessLocationUpdateCommand(inventoryView, location.toBukkit(world))); - } - } - } - } - - if ((entity.getType() == EntityType.PLAYER && !(craft instanceof SinkingCraft))) { - CraftTeleportEntityEvent e = new CraftTeleportEntityEvent(craft, entity); - Bukkit.getServer().getPluginManager().callEvent(e); - if (e.isCancelled()) - continue; - - EntityUpdateCommand eUp = new EntityUpdateCommand(entity, dx, dy, dz, 0, 0, - world, sound, volume); - updates.add(eUp); - } else if (!craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS) - || entity.getType() == EntityType.PRIMED_TNT) { - CraftTeleportEntityEvent e = new CraftTeleportEntityEvent(craft, entity); - Bukkit.getServer().getPluginManager().callEvent(e); - if (e.isCancelled()) - continue; + preventsTorpedoRocketsPilots(); + captureYield(harvestedBlocks); + } - EntityUpdateCommand eUp = new EntityUpdateCommand(entity, dx, dy, dz, 0, 0, world); - updates.add(eUp); - } - } - } else { + private void preventsTorpedoRocketsPilots() { + if (!craft.getType().getBoolProperty(CraftType.MOVE_ENTITIES) || + (craft instanceof SinkingCraft + && craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS))) { // add releaseTask without playermove to manager if (!craft.getType().getBoolProperty(CraftType.CRUISE_ON_PILOT) && !(craft instanceof SinkingCraft)) // not necessary to release cruiseonpilot crafts, because they will already be released CraftManager.getInstance().addReleaseTask(craft); + return; } - captureYield(harvestedBlocks); + + Location midpoint = new Location( + craft.getWorld(), + (oldHitBox.getMaxX() + oldHitBox.getMinX()) / 2.0, + (oldHitBox.getMaxY() + oldHitBox.getMinY()) / 2.0, + (oldHitBox.getMaxZ() + oldHitBox.getMinZ()) / 2.0); + for (Entity entity : craft.getWorld().getNearbyEntities(midpoint, + oldHitBox.getXLength() / 2.0 + 1, + oldHitBox.getYLength() / 2.0 + 2, + oldHitBox.getZLength() / 2.0 + 1 + )) { + + processHumanEntity(entity); + + if ((entity.getType() == EntityType.PLAYER && !(craft instanceof SinkingCraft))) { + CraftTeleportEntityEvent e = new CraftTeleportEntityEvent(craft, entity); + Bukkit.getServer().getPluginManager().callEvent(e); + if (e.isCancelled()) + continue; + + EntityUpdateCommand eUp = new EntityUpdateCommand(entity, dx, dy, dz, 0, 0, + world, sound, volume); + updates.add(eUp); + continue; + } + + if (craft.getType().getBoolProperty(CraftType.ONLY_MOVE_PLAYERS) + && entity.getType() != EntityType.PRIMED_TNT) { + continue; + } + + CraftTeleportEntityEvent e = new CraftTeleportEntityEvent(craft, entity); + Bukkit.getServer().getPluginManager().callEvent(e); + if (e.isCancelled()) + continue; + + EntityUpdateCommand eUp = new EntityUpdateCommand(entity, dx, dy, dz, 0, 0, world); + updates.add(eUp); + } + } + + //this part looks similar to rotationTask + //maybe can be thrown in a util class? + private void processHumanEntity(Entity entity) { + if (!(entity instanceof HumanEntity)) { + return; + } + + InventoryView inventoryView = ((HumanEntity) entity).getOpenInventory(); + if (inventoryView.getType() == InventoryType.CRAFTING) { + return; + } + + Location l = Movecraft.getInstance().getWorldHandler().getAccessLocation(inventoryView); + if (l == null) { + return; + } + + MovecraftLocation location = new MovecraftLocation(l.getBlockX(), l.getBlockY(), l.getBlockZ()); + if (oldHitBox.contains(location)) { + location = location.translate(dx, dy, dz); + updates.add(new AccessLocationUpdateCommand(inventoryView, location.toBukkit(world))); + } + } + + /** + * + * @return True if failed, false otherwise + */ + private boolean processGravity() { + //Process gravity (must be same world too) + if (!world.equals(craft.getWorld()) || !craft.getType().getBoolProperty(CraftType.USE_GRAVITY) + || craft instanceof SinkingCraft) { + return false; + } + + int incline = inclineCraft(oldHitBox); + if (incline > 0) { + boolean tooSteep = craft.getType().getIntProperty(CraftType.GRAVITY_INCLINE_DISTANCE) > -1 + && incline > craft.getType().getIntProperty(CraftType.GRAVITY_INCLINE_DISTANCE); + if (tooSteep && craft.getType().getFloatProperty(CraftType.COLLISION_EXPLOSION) <= 0F) { + fail(I18nSupport.getInternationalisedString("Translation - Failed Incline too steep")); + return true; + } + dy = tooSteep ? 0 : incline; + return false; + } + + if (isOnGround(oldHitBox)) { + return false; + } + + if (craft.getType().getBoolProperty(CraftType.CAN_HOVER)) { + MovecraftLocation midPoint = oldHitBox.getMidPoint(); + int centreMinY = oldHitBox.getMinYAt(midPoint.getX(), midPoint.getZ()); + int groundY = centreMinY; + World w = craft.getWorld(); + + while (groundY - 1 >= w.getMinHeight() + && (w.getBlockAt(midPoint.getX(), groundY - 1, midPoint.getZ()).getType().isAir() + || craft.getType().getMaterialSetProperty(CraftType.PASSTHROUGH_BLOCKS).contains( + w.getBlockAt(midPoint.getX(), groundY - 1, midPoint.getZ()).getType()))) { + groundY--; + } + + if (centreMinY - groundY > craft.getType().getIntProperty(CraftType.HOVER_LIMIT)) + dy = -1; + return false; + } + + dy = dropDistance(oldHitBox); + return false; + } + + private void processAllNetherPortals() throws ExecutionException, InterruptedException { + // proccess nether portals + if (!Settings.CraftsUseNetherPortals + || craft.getWorld().getEnvironment() == Environment.THE_END + || !world.equals(craft.getWorld())) { + return; + } + + // ensure chunks are loaded for portal checking only if change in location is + // large + Set chunksToLoad = ChunkManager.getChunks(oldHitBox, world, dx, dy, dz); + MovecraftChunk.addSurroundingChunks(chunksToLoad, 2); + ChunkManager.checkChunks(chunksToLoad); + if (!chunksToLoad.isEmpty()) + ChunkManager.syncLoadChunks(chunksToLoad).get(); + + for (MovecraftLocation oldLocation : oldHitBox) { + Location location = oldLocation.translate(dx, dy, dz).toBukkit(craft.getWorld()); + Block block = craft.getWorld().getBlockAt(location); + + if (block.getType() != Material.NETHER_PORTAL) { + continue; + } + + if (processNetherPortal(block)) { + sound = Sound.BLOCK_PORTAL_TRAVEL; + volume = 0.25f; + break; + } + + } + } + + private void processHighCraft(int minY, int maxY) { + // Only modify dy when not switching worlds + // Check if the craft is too high + boolean sameWorld = world.equals(craft.getWorld()); + CraftType craftType = craft.getType(); + if (!sameWorld) { + return; + } + + if ((int) craftType.getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_LIMIT, craft.getWorld()) < craft.getHitBox().getMinY()) { + dy = Math.min(dy, -1); + return; + } + final int perWorldMaxHeighAboveGround = (int) craftType.getPerWorldProperty(CraftType.PER_WORLD_MAX_HEIGHT_ABOVE_GROUND, world); + // world == craft.world + if (perWorldMaxHeighAboveGround <= 0) { + return; + } + + final MovecraftLocation middle = oldHitBox.getMidPoint(); + int testY; + + for (testY = minY; testY > world.getMinHeight(); testY--) { + if (!craft.getWorld().getBlockAt(middle.getX(), testY - 1, middle.getZ()).getType().isAir()) { + break; + } + } + + if (maxY - testY > perWorldMaxHeighAboveGround) + dy = Math.min(dy, -1); } private void fail(@NotNull String failMessage) { From b94e3522ca9d6d54bff07779aa88f2ac3205bb90 Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Tue, 20 Aug 2024 18:12:33 -0500 Subject: [PATCH 12/17] Revert "Use processing for wrecks (#686)" (#698) This reverts commit 9ccfe24bcbf1fc6890f3ea06a1b45507ecfe04cf. --- .../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 ---- 12 files changed, 93 insertions(+), 339 deletions(-) delete mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/features/fading/FadeTask.java delete mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckManager.java delete mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckTask.java delete mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/DeferredEffect.java delete 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 fe69859c1..d5f6eabbb 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java @@ -27,7 +27,6 @@ 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.*; @@ -58,7 +57,6 @@ public class Movecraft extends JavaPlugin { private WorldHandler worldHandler; private SmoothTeleport smoothTeleport; private AsyncManager asyncManager; - private WreckManager wreckManager; public static synchronized Movecraft getInstance() { return instance; @@ -191,10 +189,8 @@ 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); @@ -336,8 +332,4 @@ 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 7825b86e2..fc2cfe889 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java @@ -23,26 +23,24 @@ import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.async.rotation.RotationTask; import net.countercraft.movecraft.async.translation.TranslationTask; -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.config.Settings; +import net.countercraft.movecraft.craft.*; 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.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; +import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -51,8 +49,14 @@ 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) { @@ -67,6 +71,15 @@ 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(); @@ -312,11 +325,68 @@ 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 1db3db008..ec586acd2 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().getWreckManager().queueWreck(craft); + Movecraft.getInstance().getAsyncManager().addWreck(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 deleted file mode 100644 index a8ae5f1b8..000000000 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/FadeTask.java +++ /dev/null @@ -1,42 +0,0 @@ -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 deleted file mode 100644 index ee12262df..000000000 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckManager.java +++ /dev/null @@ -1,45 +0,0 @@ -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 deleted file mode 100644 index 5df495527..000000000 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/fading/WreckTask.java +++ /dev/null @@ -1,73 +0,0 @@ -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 deleted file mode 100644 index ee0864345..000000000 --- a/Movecraft/src/main/java/net/countercraft/movecraft/processing/effects/DeferredEffect.java +++ /dev/null @@ -1,36 +0,0 @@ -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 c31e93944..82aca582c 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,36 +1,16 @@ package net.countercraft.movecraft.processing.effects; import net.countercraft.movecraft.MovecraftLocation; -import net.countercraft.movecraft.mapUpdater.update.BlockCreateCommand; -import net.countercraft.movecraft.processing.MovecraftWorld; -import org.bukkit.Bukkit; +import org.bukkit.Material; 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 { - private final @NotNull MovecraftWorld world; - private final @NotNull MovecraftLocation location; - private final @NotNull BlockData data; + public SetBlockEffect(World world, MovecraftLocation location, Material material){ - 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 1f3486f43..1636dec68 100644 --- a/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java +++ b/api/src/main/java/net/countercraft/movecraft/MovecraftLocation.java @@ -88,26 +88,6 @@ 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 74378240d..ecef2e3df 100644 --- a/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java +++ b/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java @@ -4,7 +4,6 @@ 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; @@ -33,7 +32,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; @@ -123,7 +122,7 @@ public void submit(Runnable task){ }); } - public void submit(Supplier<@Nullable Effect> task){ + public void submit(Supplier 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 f12d2f458..284ddbff4 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,79 +1,24 @@ 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){ - 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); - + default @NotNull + Effect andThen(@Nullable Effect chain){ + if(chain == null){ 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 deleted file mode 100644 index f98c4c4da..000000000 --- a/api/src/main/java/net/countercraft/movecraft/util/CollectorUtils.java +++ /dev/null @@ -1,16 +0,0 @@ -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); - } -} From 9ddedc75a16e126d0af7241231ea2935fe8ccba6 Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Tue, 20 Aug 2024 19:12:46 -0500 Subject: [PATCH 13/17] 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()))); } } From 71e87b2b73fea8d942a3a82e569f7391e6a87685 Mon Sep 17 00:00:00 2001 From: Vaan1310 <61906290+Intybyte@users.noreply.github.com> Date: Sun, 25 Aug 2024 16:46:58 +0200 Subject: [PATCH 14/17] Feature/track move flyblocks (#700) * Tracking Moveblocks and Flyblocks * Better save these in a variable * Moving this out * Changed var type to Counter * Use new logic to StatusSign * Move down variables * Use Counter instead * No region comments * Clean up --- .../features/status/StatusManager.java | 24 +++++++++++- .../movecraft/features/status/StatusSign.java | 39 +++---------------- .../countercraft/movecraft/craft/Craft.java | 4 +- 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java index ed48e75d4..761acd77b 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java @@ -5,7 +5,6 @@ import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.craft.CraftManager; import net.countercraft.movecraft.craft.SinkingCraft; -import net.countercraft.movecraft.craft.datatag.CraftDataTagContainer; import net.countercraft.movecraft.craft.datatag.CraftDataTagKey; import net.countercraft.movecraft.craft.datatag.CraftDataTagRegistry; import net.countercraft.movecraft.craft.type.CraftType; @@ -68,11 +67,12 @@ private StatusUpdateTask(@NotNull Craft craft) { } @Override - public @Nullable Effect get() { + public @NotNull Effect get() { Counter materials = new Counter<>(); int nonNegligibleBlocks = 0; int nonNegligibleSolidBlocks = 0; double fuel = 0; + for (MovecraftLocation l : craft.getHitBox()) { Material type = craft.getMovecraftWorld().getMaterial(l); materials.add(type); @@ -94,8 +94,28 @@ private StatusUpdateTask(@NotNull Craft craft) { } } + Counter flyblocks = new Counter<>(); + Counter moveblocks = new Counter<>(); + for(Material material : materials.getKeySet()) { + for(RequiredBlockEntry entry : craft.getType().getRequiredBlockProperty(CraftType.FLY_BLOCKS)) { + if(entry.contains(material)) { + flyblocks.add(entry, materials.get(material) ); + break; + } + } + + for(RequiredBlockEntry entry : craft.getType().getRequiredBlockProperty(CraftType.MOVE_BLOCKS)) { + if(entry.contains(material)) { + moveblocks.add(entry, materials.get(material) ); + break; + } + } + } + craft.setDataTag(Craft.FUEL, fuel); craft.setDataTag(Craft.MATERIALS, materials); + craft.setDataTag(Craft.FLYBLOCKS, flyblocks); + craft.setDataTag(Craft.MOVEBLOCKS, moveblocks); craft.setDataTag(Craft.NON_NEGLIGIBLE_BLOCKS, nonNegligibleBlocks); craft.setDataTag(Craft.NON_NEGLIGIBLE_SOLID_BLOCKS, nonNegligibleSolidBlocks); craft.setDataTag(LAST_STATUS_CHECK, System.currentTimeMillis()); diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusSign.java index 1246c1381..d8a488576 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusSign.java @@ -1,16 +1,11 @@ package net.countercraft.movecraft.features.status; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import net.countercraft.movecraft.Movecraft; import net.countercraft.movecraft.MovecraftLocation; -import net.countercraft.movecraft.craft.BaseCraft; import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.craft.type.RequiredBlockEntry; import net.countercraft.movecraft.events.CraftDetectEvent; import net.countercraft.movecraft.events.SignTranslateEvent; -import net.countercraft.movecraft.features.status.StatusManager; import net.countercraft.movecraft.util.Counter; import net.countercraft.movecraft.util.Tags; import org.bukkit.ChatColor; @@ -25,16 +20,8 @@ import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; - public final class StatusSign implements Listener { @EventHandler @@ -82,28 +69,14 @@ public final void onSignTranslate(SignTranslateEvent event) { totalNonNegligibleWaterBlocks += add; } } - Object2IntMap displayBlocks = new Object2IntOpenHashMap<>(); - for (RequiredBlockEntry entry : craft.getType().getRequiredBlockProperty(CraftType.FLY_BLOCKS)) { - int total = 0; - for (Material material : entry.getMaterials()) { - if (materials.getKeySet().contains(material)) { - total += materials.get(material); - } - } - displayBlocks.putIfAbsent(entry, total); - } - for (RequiredBlockEntry entry : craft.getType().getRequiredBlockProperty(CraftType.MOVE_BLOCKS)) { - int total = 0; - for (Material material : entry.getMaterials()) { - if (materials.getKeySet().contains(material)) { - total += materials.get(material); - } - } - displayBlocks.putIfAbsent(entry, total); - } + + Counter displayBlocks = new Counter<>(); + displayBlocks.add(craft.getDataTag(Craft.FLYBLOCKS)); + displayBlocks.add(craft.getDataTag(Craft.MOVEBLOCKS)); + int signLine = 1; int signColumn = 0; - for (RequiredBlockEntry entry : displayBlocks.keySet()) { + for (RequiredBlockEntry entry : displayBlocks.getKeySet()) { if (entry.getMin() == 0.0) { continue; } diff --git a/api/src/main/java/net/countercraft/movecraft/craft/Craft.java b/api/src/main/java/net/countercraft/movecraft/craft/Craft.java index 2c13ad657..a1a44ee69 100644 --- a/api/src/main/java/net/countercraft/movecraft/craft/Craft.java +++ b/api/src/main/java/net/countercraft/movecraft/craft/Craft.java @@ -21,10 +21,10 @@ import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.MovecraftRotation; import net.countercraft.movecraft.TrackedLocation; -import net.countercraft.movecraft.craft.datatag.CraftDataTagContainer; import net.countercraft.movecraft.craft.datatag.CraftDataTagKey; import net.countercraft.movecraft.craft.datatag.CraftDataTagRegistry; import net.countercraft.movecraft.craft.type.CraftType; +import net.countercraft.movecraft.craft.type.RequiredBlockEntry; import net.countercraft.movecraft.processing.MovecraftWorld; import net.countercraft.movecraft.util.Counter; import net.countercraft.movecraft.util.MathUtils; @@ -47,6 +47,8 @@ public interface Craft { CraftDataTagKey> CONTACTS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "contacts"), craft -> new ArrayList<>(0)); CraftDataTagKey FUEL = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "fuel"), craft -> 0D); CraftDataTagKey> MATERIALS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "materials"), craft -> new Counter<>()); + CraftDataTagKey> FLYBLOCKS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "flyblocks"), craft -> new Counter<>()); + CraftDataTagKey> MOVEBLOCKS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "moveblocks"), craft -> new Counter<>()); CraftDataTagKey NON_NEGLIGIBLE_BLOCKS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "non-negligible-blocks"), Craft::getOrigBlockCount); CraftDataTagKey NON_NEGLIGIBLE_SOLID_BLOCKS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "non-negligible-solid-blocks"), Craft::getOrigBlockCount); From c9b06daae49ca869a1adda9f740dbf0023b9e0b5 Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Sun, 25 Aug 2024 09:54:26 -0500 Subject: [PATCH 15/17] Bump version for release --- buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts index 489d005a7..8d453cbd9 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts @@ -10,7 +10,7 @@ repositories { } group = "net.countercraft" -version = "8.0.0_beta-5_dev-2" +version = "8.0.0_beta-5" tasks.withType() { options.encoding = "UTF-8" From aa7a07146a6bf93d3bbc193e9f7edac7ad595479 Mon Sep 17 00:00:00 2001 From: Vaan1310 <61906290+Intybyte@users.noreply.github.com> Date: Sun, 25 Aug 2024 22:34:34 +0200 Subject: [PATCH 16/17] Done (#703) --- .../countercraft/movecraft/features/status/StatusManager.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java index 761acd77b..5a9068f01 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java @@ -27,7 +27,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Map; import java.util.function.Supplier; @@ -100,14 +99,12 @@ private StatusUpdateTask(@NotNull Craft craft) { for(RequiredBlockEntry entry : craft.getType().getRequiredBlockProperty(CraftType.FLY_BLOCKS)) { if(entry.contains(material)) { flyblocks.add(entry, materials.get(material) ); - break; } } for(RequiredBlockEntry entry : craft.getType().getRequiredBlockProperty(CraftType.MOVE_BLOCKS)) { if(entry.contains(material)) { moveblocks.add(entry, materials.get(material) ); - break; } } } From 61c79dda8acaaf3cd2556d5c7f8915714b1583df Mon Sep 17 00:00:00 2001 From: TylerS1066 Date: Sun, 25 Aug 2024 15:39:01 -0500 Subject: [PATCH 17/17] Paper smooth teleportation (#702) * First attempt * Second attempt * Third attempt * Working * Remove inventory access for 1.20+ * Fix world change logic * Update 1.21 implementation * Fix manoverboard * Revert "Remove inventory access for 1.20+" This reverts commit 161ad50c260be65ba3e481f17af7ff5ff2fe4cb4. * Deprecate access for 1.20+ * Fix 1.18.2 implementation --- .../commands/ManOverboardCommand.java | 5 +- .../update/EntityUpdateCommand.java | 20 ++++--- .../movecraft/SmoothTeleport.java | 2 +- .../countercraft/movecraft/WorldHandler.java | 6 +- .../movecraft/util/BukkitTeleport.java | 4 +- .../support/v1_18/ISmoothTeleport.java | 4 +- .../movecraft/compat/v1_20/IWorldHandler.java | 20 +------ .../support/v1_20/ISmoothTeleport.java | 60 ++++--------------- .../movecraft/compat/v1_21/IWorldHandler.java | 20 +------ .../support/v1_21/ISmoothTeleport.java | 60 ++++--------------- 10 files changed, 54 insertions(+), 147 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java b/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java index e3118c255..98882f49c 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/commands/ManOverboardCommand.java @@ -8,7 +8,6 @@ import net.countercraft.movecraft.events.ManOverboardEvent; import net.countercraft.movecraft.localisation.I18nSupport; import net.countercraft.movecraft.util.MathUtils; -import net.countercraft.movecraft.util.ReflectUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.command.Command; @@ -71,9 +70,11 @@ public boolean onCommand(CommandSender commandSender, Command command, String s, ManOverboardEvent event = new ManOverboardEvent(craft, telPoint); Bukkit.getServer().getPluginManager().callEvent(event); + telPoint.setYaw(player.getLocation().getYaw()); + telPoint.setPitch(player.getLocation().getPitch()); player.setVelocity(new Vector(0, 0, 0)); player.setFallDistance(0); - Movecraft.getInstance().getSmoothTeleport().teleport(player, telPoint, 0, 0); + Movecraft.getInstance().getSmoothTeleport().teleport(player, telPoint); return true; } diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java b/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java index c334f4e5b..40f7ce2b8 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/EntityUpdateCommand.java @@ -83,17 +83,21 @@ public Entity getEntity() { @Override public void doUpdate() { - Location playerLoc = entity.getLocation(); - // Use bukkit teleporting API for changing worlds because it won't be smooth anyway - if (!(entity instanceof Player) || !playerLoc.getWorld().equals(world)) { - entity.teleport(new Location(world, x + playerLoc.getX(),y + playerLoc.getY(),z + playerLoc.getZ(),yaw + playerLoc.getYaw(),pitch + playerLoc.getPitch())); - return; - } - Location location = new Location(world, playerLoc.getX() + x, playerLoc.getY() + y, playerLoc.getZ() + z); - Movecraft.getInstance().getSmoothTeleport().teleport((Player) entity, location, yaw, pitch); + Location location = entity.getLocation().add(x, y, z); + location.setYaw(location.getYaw() + yaw); + location.setPitch(location.getPitch() + pitch); + location.setWorld(world); if (sound != null) { ((Player) entity).playSound(location, sound, volume, 1.0f); } + + // Use bukkit teleporting API for changing worlds because it won't be smooth anyway + if (!(entity instanceof Player) || !entity.getLocation().getWorld().equals(world)) { + entity.teleport(location); + return; + } + + Movecraft.getInstance().getSmoothTeleport().teleport((Player) entity, location); } @Override diff --git a/api/src/main/java/net/countercraft/movecraft/SmoothTeleport.java b/api/src/main/java/net/countercraft/movecraft/SmoothTeleport.java index f6e9e898c..97e7fbb31 100644 --- a/api/src/main/java/net/countercraft/movecraft/SmoothTeleport.java +++ b/api/src/main/java/net/countercraft/movecraft/SmoothTeleport.java @@ -5,5 +5,5 @@ import org.jetbrains.annotations.NotNull; public abstract class SmoothTeleport { - public abstract void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange); + public abstract void teleport(@NotNull Player player, @NotNull Location location); } diff --git a/api/src/main/java/net/countercraft/movecraft/WorldHandler.java b/api/src/main/java/net/countercraft/movecraft/WorldHandler.java index bbf829daf..3b72e569f 100644 --- a/api/src/main/java/net/countercraft/movecraft/WorldHandler.java +++ b/api/src/main/java/net/countercraft/movecraft/WorldHandler.java @@ -15,8 +15,10 @@ public abstract class WorldHandler { public abstract void translateCraft(@NotNull Craft craft, @NotNull MovecraftLocation newLocation, @NotNull World world); public abstract void setBlockFast(@NotNull Location location, @NotNull BlockData data); public abstract void setBlockFast(@NotNull Location location, @NotNull MovecraftRotation rotation, @NotNull BlockData data); - public abstract @Nullable Location getAccessLocation(@NotNull InventoryView inventoryView); - public abstract void setAccessLocation(@NotNull InventoryView inventoryView, @NotNull Location location); + @Deprecated(forRemoval = true) + public abstract @Nullable Location getAccessLocation(@NotNull InventoryView inventoryView); // Not needed for 1.20+, remove when dropping support for 1.18.2 + @Deprecated(forRemoval = true) + public abstract void setAccessLocation(@NotNull InventoryView inventoryView, @NotNull Location location); // Not needed for 1.20+, remove when dropping support for 1.18.2 public static @NotNull String getPackageName(@NotNull String minecraftVersion) { String[] parts = minecraftVersion.split("\\."); diff --git a/api/src/main/java/net/countercraft/movecraft/util/BukkitTeleport.java b/api/src/main/java/net/countercraft/movecraft/util/BukkitTeleport.java index 7bc69dc9e..702d3b26f 100644 --- a/api/src/main/java/net/countercraft/movecraft/util/BukkitTeleport.java +++ b/api/src/main/java/net/countercraft/movecraft/util/BukkitTeleport.java @@ -7,9 +7,7 @@ public class BukkitTeleport extends SmoothTeleport { @Override - public void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { - location.setYaw(player.getLocation().getYaw() + yawChange); - location.setPitch(player.getLocation().getPitch() + pitchChange); + public void teleport(@NotNull Player player, @NotNull Location location) { player.teleport(location); } } diff --git a/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/ISmoothTeleport.java b/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/ISmoothTeleport.java index 5c8beb91f..be1b7b0be 100644 --- a/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/ISmoothTeleport.java +++ b/v1_18/src/main/java/net/countercraft/movecraft/support/v1_18/ISmoothTeleport.java @@ -75,10 +75,12 @@ public ISmoothTeleport() throws ClassNotFoundException, NoSuchMethodException, N pitchField = ReflectUtils.getField(entityClass, "aA"); // yRot } - public void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { + public void teleport(@NotNull Player player, @NotNull Location location) { double x = location.getX(); double y = location.getY(); double z = location.getZ(); + float yawChange = location.getYaw() - player.getLocation().getYaw(); + float pitchChange = player.getLocation().getPitch() - location.getPitch(); Object handle = ReflectUtils.getHandle(player); try { positionMethod.invoke(handle, x, y, z, yawField.get(handle), pitchField.get(handle)); diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java index 676ea054f..cd2a5565a 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/IWorldHandler.java @@ -262,29 +262,13 @@ public void setBlockFast(@NotNull Location location, @NotNull MovecraftRotation @Override public @Nullable Location getAccessLocation(@NotNull InventoryView inventoryView) { - AbstractContainerMenu menu = ((CraftInventoryView) inventoryView).getHandle(); - Field field = UnsafeUtils.getFieldOfType(ContainerLevelAccess.class, menu.getClass()); - if (field != null) { - try { - field.setAccessible(true); - return ((ContainerLevelAccess) field.get(menu)).getLocation(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } + // Not needed for 1.20+, remove when dropping support for 1.18.2 return null; } @Override public void setAccessLocation(@NotNull InventoryView inventoryView, @NotNull Location location) { - if (location.getWorld() == null) - return; - ServerLevel level = ((CraftWorld) location.getWorld()).getHandle(); - BlockPos position = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - ContainerLevelAccess access = ContainerLevelAccess.create(level, position); - - AbstractContainerMenu menu = ((CraftInventoryView) inventoryView).getHandle(); - UnsafeUtils.trySetFieldOfType(ContainerLevelAccess.class, menu, access); + // Not needed for 1.20+, remove when dropping support for 1.18.2 } private void moveBlockEntity(@NotNull Level nativeWorld, @NotNull BlockPos newPosition, @NotNull BlockEntity tile) { diff --git a/v1_20/src/main/java/net/countercraft/movecraft/support/v1_20/ISmoothTeleport.java b/v1_20/src/main/java/net/countercraft/movecraft/support/v1_20/ISmoothTeleport.java index f1224b7ea..ca4a4731c 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/support/v1_20/ISmoothTeleport.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/support/v1_20/ISmoothTeleport.java @@ -1,57 +1,23 @@ package net.countercraft.movecraft.support.v1_20; +import io.papermc.paper.entity.TeleportFlag; import net.countercraft.movecraft.SmoothTeleport; -import net.countercraft.movecraft.util.ReflectUtils; -import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.world.entity.RelativeMovement; -import net.minecraft.world.phys.Vec3; import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Field; -import java.util.Set; - -/** - * Code derived from code taken with permission from MicleBrick - * https://www.spigotmc.org/threads/teleport-player-smoothly.317416/ - * Used for 1.20.6 - */ public class ISmoothTeleport extends SmoothTeleport { - private final Field teleportPosField; - private final Field teleportAwaitField; - private final Field awaitingTeleportTimeField; - private final Field tickCountField; - - public ISmoothTeleport() throws NoSuchFieldException, ClassNotFoundException { - teleportPosField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "F"); // awaitingPositionFromClient - teleportAwaitField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "G"); // awaitingTeleport - awaitingTeleportTimeField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "H"); // awaitingTeleportTime - tickCountField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "o"); // tickCount - } - - public void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { - double x = location.getX(); - double y = location.getY(); - double z = location.getZ(); - ServerPlayer handle = (ServerPlayer) ReflectUtils.getHandle(player); - - try { - handle.absMoveTo(x, y, z, handle.getXRot(), handle.getYRot()); - ServerGamePacketListenerImpl connection = handle.connection; - teleportPosField.set(connection, new Vec3(x, y, z)); - int teleportAwait = teleportAwaitField.getInt(connection) + 1; - if (teleportAwait == Integer.MAX_VALUE) - teleportAwait = 0; - teleportAwaitField.setInt(connection, teleportAwait); - awaitingTeleportTimeField.set(connection, tickCountField.get(connection)); - - ClientboundPlayerPositionPacket packet = new ClientboundPlayerPositionPacket(x, y, z, yawChange, pitchChange, Set.of(RelativeMovement.X_ROT, RelativeMovement.Y_ROT), teleportAwait); - connection.send(packet); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } + public void teleport(@NotNull Player player, @NotNull Location location) { + player.teleport( + location, + TeleportFlag.Relative.X, + TeleportFlag.Relative.Y, + TeleportFlag.Relative.Z, + TeleportFlag.Relative.PITCH, + TeleportFlag.Relative.YAW, + TeleportFlag.EntityState.RETAIN_OPEN_INVENTORY, + TeleportFlag.EntityState.RETAIN_VEHICLE, + TeleportFlag.EntityState.RETAIN_PASSENGERS + ); } } diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java index 4a48783c8..30f1e8182 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/IWorldHandler.java @@ -257,29 +257,13 @@ public void setBlockFast(@NotNull Location location, @NotNull MovecraftRotation @Override public @Nullable Location getAccessLocation(@NotNull InventoryView inventoryView) { - AbstractContainerMenu menu = ((CraftInventoryView) inventoryView).getHandle(); - Field field = UnsafeUtils.getFieldOfType(ContainerLevelAccess.class, menu.getClass()); - if (field != null) { - try { - field.setAccessible(true); - return ((ContainerLevelAccess) field.get(menu)).getLocation(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } + // Not needed for 1.20+, remove when dropping support for 1.18.2 return null; } @Override public void setAccessLocation(@NotNull InventoryView inventoryView, @NotNull Location location) { - if (location.getWorld() == null) - return; - ServerLevel level = ((CraftWorld) location.getWorld()).getHandle(); - BlockPos position = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - ContainerLevelAccess access = ContainerLevelAccess.create(level, position); - - AbstractContainerMenu menu = ((CraftInventoryView) inventoryView).getHandle(); - UnsafeUtils.trySetFieldOfType(ContainerLevelAccess.class, menu, access); + // Not needed for 1.20+, remove when dropping support for 1.18.2 } private void moveBlockEntity(@NotNull Level nativeWorld, @NotNull BlockPos newPosition, @NotNull BlockEntity tile) { diff --git a/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java b/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java index 0d77c7f9a..edd20345f 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/support/v1_21/ISmoothTeleport.java @@ -1,57 +1,23 @@ package net.countercraft.movecraft.support.v1_21; +import io.papermc.paper.entity.TeleportFlag; import net.countercraft.movecraft.SmoothTeleport; -import net.countercraft.movecraft.util.ReflectUtils; -import net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.world.entity.RelativeMovement; -import net.minecraft.world.phys.Vec3; import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Field; -import java.util.Set; - -/** - * Code derived from code taken with permission from MicleBrick - * https://www.spigotmc.org/threads/teleport-player-smoothly.317416/ - * Used for 1.21.1 - */ public class ISmoothTeleport extends SmoothTeleport { - private final Field teleportPosField; - private final Field teleportAwaitField; - private final Field awaitingTeleportTimeField; - private final Field tickCountField; - - public ISmoothTeleport() throws NoSuchFieldException, ClassNotFoundException { - teleportPosField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "F"); // awaitingPositionFromClient - teleportAwaitField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "G"); // awaitingTeleport - awaitingTeleportTimeField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "H"); // awaitingTeleportTime - tickCountField = ReflectUtils.getField(ServerGamePacketListenerImpl.class, "o"); // tickCount - } - - public void teleport(Player player, @NotNull Location location, float yawChange, float pitchChange) { - double x = location.getX(); - double y = location.getY(); - double z = location.getZ(); - ServerPlayer handle = (ServerPlayer) ReflectUtils.getHandle(player); - - try { - handle.absMoveTo(x, y, z, handle.getXRot(), handle.getYRot()); - ServerGamePacketListenerImpl connection = handle.connection; - teleportPosField.set(connection, new Vec3(x, y, z)); - int teleportAwait = teleportAwaitField.getInt(connection) + 1; - if (teleportAwait == Integer.MAX_VALUE) - teleportAwait = 0; - teleportAwaitField.setInt(connection, teleportAwait); - awaitingTeleportTimeField.set(connection, tickCountField.get(connection)); - - ClientboundPlayerPositionPacket packet = new ClientboundPlayerPositionPacket(x, y, z, yawChange, pitchChange, Set.of(RelativeMovement.X_ROT, RelativeMovement.Y_ROT), teleportAwait); - connection.send(packet); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } + public void teleport(@NotNull Player player, @NotNull Location location) { + player.teleport( + location, + TeleportFlag.Relative.X, + TeleportFlag.Relative.Y, + TeleportFlag.Relative.Z, + TeleportFlag.Relative.PITCH, + TeleportFlag.Relative.YAW, + TeleportFlag.EntityState.RETAIN_OPEN_INVENTORY, + TeleportFlag.EntityState.RETAIN_VEHICLE, + TeleportFlag.EntityState.RETAIN_PASSENGERS + ); } }