From 9736b577d5536b9b23ee2f931f7f7102bf5fd398 Mon Sep 17 00:00:00 2001 From: Dawson <30784509+funkemunky@users.noreply.github.com> Date: Sun, 25 Sep 2022 13:28:35 -0400 Subject: [PATCH] implementing new bb fixes --- .../check/impl/movement/speed/Horizontal.java | 44 ++++---- .../brighten/ac/command/AnticheatCommand.java | 75 ++----------- .../ac/data/info/BlockInformation.java | 9 +- .../brighten/ac/handler/BBRevealHandler.java | 100 ++++++++++++++++++ .../brighten/ac/listener/JoinListener.java | 4 - .../ac/packet/wrapper/impl/Processor_18.java | 2 +- .../java/dev/brighten/ac/utils/Helper.java | 46 ++++++++ .../brighten/ac/utils/world/BlockData.java | 2 +- 8 files changed, 189 insertions(+), 93 deletions(-) create mode 100644 src/main/java/dev/brighten/ac/handler/BBRevealHandler.java diff --git a/src/main/java/dev/brighten/ac/check/impl/movement/speed/Horizontal.java b/src/main/java/dev/brighten/ac/check/impl/movement/speed/Horizontal.java index a897967..64a8e58 100644 --- a/src/main/java/dev/brighten/ac/check/impl/movement/speed/Horizontal.java +++ b/src/main/java/dev/brighten/ac/check/impl/movement/speed/Horizontal.java @@ -9,6 +9,8 @@ import dev.brighten.ac.packet.ProtocolVersion; import dev.brighten.ac.packet.wrapper.in.WPacketPlayInFlying; import dev.brighten.ac.utils.*; import dev.brighten.ac.utils.math.IntVector; +import dev.brighten.ac.utils.timer.Timer; +import dev.brighten.ac.utils.timer.impl.TickTimer; import dev.brighten.ac.utils.world.types.SimpleCollisionBox; import dev.brighten.ac.utils.wrapper.Wrapper; import lombok.AllArgsConstructor; @@ -26,6 +28,7 @@ public class Horizontal extends Check { private boolean lastLastClientGround, stepped; private float buffer, vbuffer; private boolean maybeSkippedPos; + private Timer lastSkipPos = new TickTimer(); private int lastFlying; private KLocation previousFrom; @@ -49,7 +52,8 @@ public class Horizontal extends Check { || player.getInfo().isGeneralCancel() || player.getBlockInfo().onClimbable || player.getInfo().lastLiquid.isNotPassed(2)) { - motionX = new Vector(player.getMovement().getDeltaX(), player.getMovement().getDeltaY(), player.getMovement().getDeltaZ()); + motionX = new Vector(player.getMovement().getDeltaX(), player.getMovement().getDeltaY(), + player.getMovement().getDeltaZ()); break check; } @@ -247,9 +251,8 @@ public class Horizontal extends Check { // 1.9+ changes from 0.05 to 0.03 double protocolOffset = player.getPlayerVersion().isBelow(ProtocolVersion.V1_9) ? 0.05D : 0.03D; - for (d6 = protocolOffset; lmotionX != 0.0D && Helper.blockCollisions( - player.getBlockInfo().blocks, box.copy() - .offset(lmotionX, 0 ,0), Materials.SOLID).isEmpty(); originalX = lmotionX) { + for (d6 = protocolOffset; lmotionX != 0.0D && Helper.getCollisions(player, box.copy() + .offset(lmotionX, 0 ,0)).isEmpty(); originalX = lmotionX) { if (lmotionX < d6 && lmotionX >= -d6) { lmotionX = 0.0D; } else if (lmotionX > 0.0D) { @@ -259,9 +262,8 @@ public class Horizontal extends Check { } } - for (; lmotionZ != 0.0D && Helper.blockCollisions( - player.getBlockInfo().blocks, box.copy() - .offset(0, 0 ,lmotionZ), Materials.SOLID).isEmpty(); originalZ = lmotionZ) { + for (; lmotionZ != 0.0D && Helper.getCollisions(player, box.copy() + .offset(0, 0 ,lmotionZ)).isEmpty(); originalZ = lmotionZ) { if (lmotionZ < d6 && lmotionZ >= -d6) { lmotionZ = 0.0D; } else if (lmotionZ > 0.0D) { @@ -271,9 +273,8 @@ public class Horizontal extends Check { } } - for (; lmotionX != 0.0D && lmotionZ != 0.0D && Helper.blockCollisions - (player.getBlockInfo().blocks, box.copy() - .offset(lmotionX, -1.0 ,lmotionZ), Materials.SOLID).isEmpty(); + for (; lmotionX != 0.0D && lmotionZ != 0.0D && Helper.getCollisions(player, box.copy() + .offset(lmotionX, -1.0 ,lmotionZ)).isEmpty(); originalZ = lmotionZ) { if (lmotionX < d6 && lmotionX >= -d6) { lmotionX = 0.0D; @@ -296,9 +297,8 @@ public class Horizontal extends Check { tagsBuilder.addTag("sneak-edge"); } - List collisionBoxes = Helper.toCollisionsDowncasted(Helper - .blockCollisions(player.getBlockInfo().blocks, box.copy() - .addCoord(lmotionX ,lmotionY, lmotionZ), Materials.SOLID)); + List collisionBoxes = Helper.getCollisions(player, box.copy() + .addCoord(lmotionX ,lmotionY, lmotionZ)); for (SimpleCollisionBox blockBox : collisionBoxes) { lmotionY = blockBox.calculateYOffset(box, lmotionY); @@ -319,7 +319,6 @@ public class Horizontal extends Check { box = box.offset(0, 0, lmotionZ); - boolean didStepCalc = false; if(stepped && (lmotionX != originalX || lmotionZ != originalZ)) { double d11 = lmotionX; double d7 = lmotionY; @@ -329,10 +328,8 @@ public class Horizontal extends Check { box = player.getMovement().getFrom().getBox().copy(); lmotionY = 0.6; //Step height - List list = Helper.toCollisionsDowncasted(Helper - .blockCollisions(player.getBlockInfo().blocks, - box.copy().addCoord(originalX, lmotionY, originalZ), - Materials.SOLID)); + List list = Helper.getCollisions(player, + box.copy().addCoord(originalX, lmotionY, originalZ)); SimpleCollisionBox axisalignedbb4 = box; SimpleCollisionBox axisalignedbb5 = axisalignedbb4.copy().addCoord(originalX, 0.0D, originalZ); double d9 = lmotionY; @@ -492,7 +489,8 @@ public class Horizontal extends Check { } else debug("bad movement"); } else if (buffer > 0) buffer -= 0.05f; - if(smallestDeltaY > 1E-10 && !maybeSkippedPos) { + if(smallestDeltaY > 1E-10 && !maybeSkippedPos + && !player.getBlockInfo().onStairs && !player.getBlockInfo().nearSteppableEntity) { double finalSmallestDeltaY = smallestDeltaY; if(++vbuffer > 1) { cancel(); @@ -517,6 +515,10 @@ public class Horizontal extends Check { } lastFlying = player.getPlayerTick(); } + + if(maybeSkippedPos) { + lastSkipPos.reset(); + } lastLastClientGround = player.getMovement().getFrom().isOnGround(); previousFrom = player.getMovement().getFrom().getLoc().clone(); }; @@ -524,7 +526,7 @@ public class Horizontal extends Check { private double getPrecision() { if(player.getBlockInfo().onSoulSand) { return 5E-4; - } else if(maybeSkippedPos) { + } else if(lastSkipPos.isNotPassed(2)) { return 0.005; } return 5E-13; @@ -585,7 +587,7 @@ public class Horizontal extends Check { } private static boolean[] getSneakingIteration(boolean sprinting) { - return new boolean[] {false}; + return new boolean[] {true, false}; } private static boolean[] getJumpingIteration(boolean onGround) { diff --git a/src/main/java/dev/brighten/ac/command/AnticheatCommand.java b/src/main/java/dev/brighten/ac/command/AnticheatCommand.java index 72953f7..149e6b0 100644 --- a/src/main/java/dev/brighten/ac/command/AnticheatCommand.java +++ b/src/main/java/dev/brighten/ac/command/AnticheatCommand.java @@ -8,7 +8,7 @@ import dev.brighten.ac.Anticheat; import dev.brighten.ac.check.Check; import dev.brighten.ac.check.CheckData; import dev.brighten.ac.data.APlayer; -import dev.brighten.ac.handler.entity.FakeMob; +import dev.brighten.ac.handler.BBRevealHandler; import dev.brighten.ac.messages.Messages; import dev.brighten.ac.packet.handler.HandlerAbstract; import dev.brighten.ac.utils.Color; @@ -20,19 +20,17 @@ import dev.brighten.ac.utils.msg.ChatBuilder; import dev.brighten.ac.utils.reflections.Reflections; import dev.brighten.ac.utils.reflections.types.WrappedClass; import dev.brighten.ac.utils.reflections.types.WrappedMethod; -import io.netty.buffer.Unpooled; import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongList; import lombok.SneakyThrows; import lombok.val; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.TextComponent; -import net.minecraft.server.v1_8_R3.PacketDataSerializer; -import net.minecraft.server.v1_8_R3.PacketPlayOutCustomPayload; import net.minecraft.server.v1_8_R3.PacketPlayOutTitle; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.craftbukkit.v1_8_R3.util.CraftChatMessage; -import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.plugin.InvalidDescriptionException; import org.bukkit.plugin.Plugin; @@ -208,47 +206,13 @@ public class AnticheatCommand extends BaseCommand { pl.spigot().sendMessage(Messages.ALERTS_ON); } } - - @Subcommand("spawnbot") - @CommandPermission("anticheat.command.spawnbot") - @Description("Spawn test bot") - public void onBot(APlayer player) { - FakeMob fakePlayer = new FakeMob(EntityType.ZOMBIE); - - fakePlayer.spawn(true, player.getBukkitPlayer().getLocation(), player); - - player.getBukkitPlayer().sendMessage(Color.Green + "Spawned entity with ID: " + fakePlayer.getEntityId()); - } - - @Subcommand("botinvis") - @Syntax("[id] [boolean]") - @CommandPermission("anticheat.command.botinvis") - @Description("Shit") - public void onInvis(CommandSender sender, int botId, boolean invis) { - FakeMob player = Anticheat.INSTANCE.getFakeTracker().getEntityById(botId); - - if(player == null) { - sender.sendMessage(Color.Red + "Bot with ID " + botId + " does not exist!"); - return; - } - - player.setInvisible(invis); - sender.sendMessage(Color.Green + (invis ? "Made invis" : "Removed invis") + " on bot " + botId); - } - - @Subcommand("despawnbot") - @CommandPermission("anticheat.command.despawnBot") - @Syntax("[botId]") - public void despawnBot(CommandSender sender, int botId) { - FakeMob player = Anticheat.INSTANCE.getFakeTracker().getEntityById(botId); - - if(player == null) { - sender.sendMessage(Color.Red + "Bot with ID " + botId + " does not exist!"); - return; - } - - player.despawn(); - sender.sendMessage(Color.Green + "Despawned bot!"); + @Subcommand("wand") + @CommandPermission("anticheat.command.wand") + @Description("Receive a magic bounding box wand") + public void onWand(Player player) { + BBRevealHandler.INSTANCE.giveWand(player); + player.spigot().sendMessage(new ComponentBuilder( + "You've been given a very special wand. Handle it responsibly.").color(ChatColor.GREEN).create()); } @Subcommand("logs") @@ -341,25 +305,6 @@ public class AnticheatCommand extends BaseCommand { }); } - @Subcommand("runtest") - public void onCommand(Player player) { - long start = System.currentTimeMillis(); - PacketDataSerializer serializer = new PacketDataSerializer(Unpooled.buffer()); - serializer.writeLong(start); - PacketPlayOutCustomPayload payload = new PacketPlayOutCustomPayload("Time|Send", serializer); - - HandlerAbstract.getHandler().sendPacket(player, payload); - Anticheat.INSTANCE.getPlayerRegistry().getPlayer(player.getUniqueId()).ifPresent(aplayer -> { - aplayer.runInstantAction(ka -> { - if(!ka.isEnd()) { - long transDelta = System.currentTimeMillis() - start; - - player.sendMessage("Transaction delta: " + transDelta + "ms"); - } - }); - }); - } - @Subcommand("debug") @CommandCompletion("@checks|none @players") @Description("Debug a player") diff --git a/src/main/java/dev/brighten/ac/data/info/BlockInformation.java b/src/main/java/dev/brighten/ac/data/info/BlockInformation.java index 4c33bdd..4ad90a2 100644 --- a/src/main/java/dev/brighten/ac/data/info/BlockInformation.java +++ b/src/main/java/dev/brighten/ac/data/info/BlockInformation.java @@ -27,6 +27,7 @@ public class BlockInformation { public final List aboveCollisions = Collections.synchronizedList(new ArrayList<>()), belowCollisions = Collections.synchronizedList(new ArrayList<>()); public final List blocks = Collections.synchronizedList(new ArrayList<>()); + public final List entityCollisionBoxes = new ArrayList<>(); //Caching material private final Material cobweb = XMaterial.COBWEB.parseMaterial(), rosebush = XMaterial.ROSE_BUSH.parseMaterial(), @@ -50,6 +51,7 @@ public class BlockInformation { double dh = player.getMovement().getDeltaXZ() * 2; blocks.clear(); + entityCollisionBoxes.clear(); player.getInfo().setServerGround(false); player.getInfo().setNearGround(false); @@ -138,8 +140,9 @@ public class BlockInformation { for (Material type : types) { if (type != Material.AIR) { + IntVector vec = new IntVector(x, y, z); CollisionBox blockBox = BlockData.getData(type) - .getBox(world, new IntVector(x, y, z), player.getPlayerVersion()); + .getBox(world, vec, player.getPlayerVersion()); // Checking of within boundsForCollision if(x >= min.getX() && x <= max.getX() && y >= min.getY() && y <= max.getY() && z >= min.getZ() && z <= max.getZ()) { @@ -356,6 +359,10 @@ public class BlockInformation { player.getInfo().getLastEntityCollision().reset(); } + if(entityBox.isCollided(normalBox.copy().expand(0.25))) { + entityCollisionBoxes.add(entityBox); + } + if(entityBox.isCollided(normalBox.copy().expand(0.1, 0.1, 0.1))) { nearSteppableEntity = true; } diff --git a/src/main/java/dev/brighten/ac/handler/BBRevealHandler.java b/src/main/java/dev/brighten/ac/handler/BBRevealHandler.java new file mode 100644 index 0000000..7c797cf --- /dev/null +++ b/src/main/java/dev/brighten/ac/handler/BBRevealHandler.java @@ -0,0 +1,100 @@ +package dev.brighten.ac.handler; + +import dev.brighten.ac.Anticheat; +import dev.brighten.ac.packet.ProtocolVersion; +import dev.brighten.ac.packet.wrapper.objects.EnumParticle; +import dev.brighten.ac.utils.Helper; +import dev.brighten.ac.utils.ItemBuilder; +import dev.brighten.ac.utils.annotation.Init; +import dev.brighten.ac.utils.world.BlockData; +import dev.brighten.ac.utils.world.EntityData; +import dev.brighten.ac.utils.world.types.SimpleCollisionBox; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.ComponentBuilder; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@Init +public class BBRevealHandler implements Listener { + + private final Set blocksToShow = new HashSet<>(); + private final Set entitiesToShow = new HashSet<>(); + + public static BBRevealHandler INSTANCE; + + private static final ItemStack wand = new ItemBuilder(Material.BLAZE_ROD).name("&6Box Wand") + .amount(1).build(); + + public BBRevealHandler() { + INSTANCE = this; + runShowTask(); + } + + @EventHandler + public void onInteract(PlayerInteractEvent event) { + if(!event.getPlayer().getItemInHand().isSimilar(wand)) return; + + if(event.getAction() == Action.RIGHT_CLICK_BLOCK) { + if(blocksToShow.contains(event.getClickedBlock())) { + blocksToShow.remove(event.getClickedBlock()); + event.getPlayer().spigot().sendMessage(new ComponentBuilder("No longer showing block.") + .color(ChatColor.RED).create()); + } else { + blocksToShow.add(event.getClickedBlock()); + event.getPlayer().spigot().sendMessage(new ComponentBuilder("Now showing block.") + .color(ChatColor.GREEN).create()); + } + } + } + + @EventHandler + public void onInteract(PlayerInteractEntityEvent event) { + if(!event.getPlayer().getItemInHand().isSimilar(wand)) return; + + if(entitiesToShow.contains(event.getRightClicked())) { + entitiesToShow.remove(event.getRightClicked()); + event.getPlayer().spigot().sendMessage(new ComponentBuilder("No longer showing entity " + + event.getRightClicked().getName() + ".") + .color(ChatColor.RED).create()); + } else { + entitiesToShow.add(event.getRightClicked()); + event.getPlayer().spigot().sendMessage(new ComponentBuilder("Now showing entity " + + event.getRightClicked().getName() + ".") + .color(ChatColor.GREEN).create()); + } + } + + public void giveWand(Player player) { + player.getInventory().addItem(wand); + } + + private void runShowTask() { + Anticheat.INSTANCE.getScheduler().scheduleAtFixedRate(() -> { + blocksToShow.forEach(b -> BlockData.getData(b.getType()).getBox(b, ProtocolVersion.getGameVersion()) + .draw(EnumParticle.FLAME, Bukkit.getOnlinePlayers().toArray(new Player[0]))); + entitiesToShow.forEach(e -> { + SimpleCollisionBox box; + if((box =Helper.getEntityCollision(e)) != null) { + box.draw(EnumParticle.FLAME, Bukkit.getOnlinePlayers().toArray(new Player[0])); + } else { + EntityData.getEntityBox(e.getLocation(), e) + .draw(EnumParticle.FLAME, Bukkit.getOnlinePlayers().toArray(new Player[0])); + } + }); + }, 3000, 500, TimeUnit.MILLISECONDS); + } + +} diff --git a/src/main/java/dev/brighten/ac/listener/JoinListener.java b/src/main/java/dev/brighten/ac/listener/JoinListener.java index d3ce1bc..d8af2b5 100644 --- a/src/main/java/dev/brighten/ac/listener/JoinListener.java +++ b/src/main/java/dev/brighten/ac/listener/JoinListener.java @@ -9,7 +9,6 @@ import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutEntityMetadata; import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutNamedEntitySpawn; import dev.brighten.ac.utils.RunUtils; import dev.brighten.ac.utils.annotation.Init; -import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -41,9 +40,6 @@ public class JoinListener implements Listener { if(watchedObject.getDataValueId() == 6 && watchedObject.getWatchedObject() instanceof Float) { watchedObject.setWatchedObject(1f); - Bukkit.broadcastMessage("Set watched object and sending packet:" - + watchedObject.getWatchedObject()); - HandlerAbstract.getHandler().sendPacket(player, packet.getPacket()); event.setCancelled(true); break; diff --git a/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java b/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java index 3429e08..165e98e 100644 --- a/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java +++ b/src/main/java/dev/brighten/ac/packet/wrapper/impl/Processor_18.java @@ -686,7 +686,7 @@ public class Processor_18 implements PacketConverter { @Override public WPacketPlayOutRemoveEntityEffect processRemoveEffect(Object object) { PacketPlayOutRemoveEntityEffect packet = (PacketPlayOutRemoveEntityEffect) object; - + PacketDataSerializer serializer = serialize(packet); WPacketPlayOutRemoveEntityEffect ree = WPacketPlayOutRemoveEntityEffect.builder() diff --git a/src/main/java/dev/brighten/ac/utils/Helper.java b/src/main/java/dev/brighten/ac/utils/Helper.java index 5338bf9..149a4b9 100644 --- a/src/main/java/dev/brighten/ac/utils/Helper.java +++ b/src/main/java/dev/brighten/ac/utils/Helper.java @@ -1,15 +1,19 @@ package dev.brighten.ac.utils; +import dev.brighten.ac.data.APlayer; import dev.brighten.ac.packet.ProtocolVersion; import dev.brighten.ac.packet.handler.HandlerAbstract; import dev.brighten.ac.packet.wrapper.objects.EnumParticle; import dev.brighten.ac.packet.wrapper.out.WPacketPlayOutWorldParticles; +import dev.brighten.ac.utils.math.IntVector; import dev.brighten.ac.utils.world.BlockData; import dev.brighten.ac.utils.world.CollisionBox; import dev.brighten.ac.utils.world.types.RayCollision; import dev.brighten.ac.utils.world.types.SimpleCollisionBox; +import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.util.Vector; @@ -230,6 +234,48 @@ public class Helper { return collisionBoxes; } + public static List getCollisions(APlayer player, SimpleCollisionBox collisionBox) { + return getCollisions(player, collisionBox, Materials.SOLID); + } + + public static SimpleCollisionBox getEntityCollision(Entity entity) { + return new SimpleCollisionBox(ReflectionsUtil.getBoundingBox(entity)); + } + + public static List getCollisions(APlayer player, SimpleCollisionBox collisionBox, int mask) { + int x1 = (int) Math.floor(collisionBox.minX); + int y1 = (int) Math.floor(collisionBox.minY); + int z1 = (int) Math.floor(collisionBox.minZ); + int x2 = (int) Math.floor(collisionBox.maxX + 1); + int y2 = (int) Math.floor(collisionBox.maxY + 1); + int z2 = (int) Math.floor(collisionBox.maxZ + 1); + List collisionBoxes = new ArrayList<>(); + for (int x = x1; x < x2; ++x) + for (int y = y1 - 1; y < y2; ++y) + for (int z = z1; z < z2; ++z) { + IntVector vec = new IntVector(x, y, z); + Material type = player.getBlockUpdateHandler().getPossibleMaterials(vec).getLast(); + + if(type != Material.AIR && Materials.checkFlag(type, mask)) { + CollisionBox box = BlockData.getData(type) + .getBox(player.getBukkitPlayer().getWorld(), vec, ProtocolVersion.getGameVersion()); + + if(box.isIntersected(collisionBox)) { + box.downCast(collisionBoxes); + } + } + } + + for (Entity entity : player.getInfo().getNearbyEntities()) { + SimpleCollisionBox entityCollisionBox = new SimpleCollisionBox(ReflectionsUtil.getBoundingBox(entity)); + + if(entityCollisionBox.isIntersected(collisionBox)) + entityCollisionBox.downCast(collisionBoxes); + } + + return collisionBoxes; + } + public static List getBlocksNearby2(World world, SimpleCollisionBox collisionBox, int mask) { int x1 = (int) Math.floor(collisionBox.minX); int y1 = (int) Math.floor(collisionBox.minY); diff --git a/src/main/java/dev/brighten/ac/utils/world/BlockData.java b/src/main/java/dev/brighten/ac/utils/world/BlockData.java index e5f3888..fbd29d6 100644 --- a/src/main/java/dev/brighten/ac/utils/world/BlockData.java +++ b/src/main/java/dev/brighten/ac/utils/world/BlockData.java @@ -48,7 +48,7 @@ public enum BlockData { _BREWINGSTAND(new ComplexCollisionBox( new SimpleCollisionBox(0, 0, 0, 1, 0.125, 1), //base new SimpleCollisionBox(0.4375, 0.0, 0.4375, 0.5625, 0.875, 0.5625) //top - ), XMaterial.BREWING_STAND.parseMaterial()), + ), Material.BREWING_STAND), _RAIL((protocol, b) -> ReflectionsUtil.getBlockBoundingBox(b).toCollisionBox(),Arrays.stream(Material.values()) .filter(mat -> mat.name().toLowerCase().contains("rail"))