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