Aqua 13
World
Movement
Scaffold โ Full Source Analysis
FULL CODE REPRESENTATION โ This page contains the complete, unmodified source code of Aqua 13's Scaffold module. Certain sections (event system, settings framework, utility classes) are part of the Aqua client base and are marked as CLIENT BASE. These are collapsed into the source for full-context representation but originate from the client's framework, not the scaffold logic itself. See the original source at the link above for the authoritative file.
Packets Used by This Module
- C08PacketPlayerBlockPlacement โ Sends block placement to the server (the actual "placing" action)
- C0APacketAnimation โ Sends arm swing animation (silent swing via packet instead of client-side swingItem)
- C09PacketHeldItemChange โ Silent slot switching (via EventSycItem interception)
- C03PacketPlayer (C04/C05/C06) โ Movement packets affected by tower modes (motionY manipulation)
Key MCP Classes Referenced
net.minecraft.client.Minecraftโ Game instance (mc.thePlayer, mc.theWorld, mc.playerController, mc.getNetHandler())net.minecraft.client.multiplayer.PlayerControllerMPโ onPlayerRightClick() โ triggers C08 placementnet.minecraft.network.play.client.C08PacketPlayerBlockPlacementโ Block placement packetnet.minecraft.network.play.client.C0APacketAnimationโ Arm swing animation packetnet.minecraft.util.BlockPosโ 3D block coordinate (x, y, z)net.minecraft.util.EnumFacingโ Direction enum (UP, DOWN, NORTH, etc.)net.minecraft.util.MovingObjectPositionโ Ray-trace result (what block the player is looking at)net.minecraft.util.AxisAlignedBBโ Bounding box for ray-trace collision checks
Package & ImportsLines 1-51
| Package declaration โ this file lives in Aqua's world module package | ||
| 1 | package intent.AquaDev.aqua.modules.world; | |
| CLIENT BASE โ Aqua's event/settings framework imports. These are part of the client infrastructure, not scaffold-specific logic. | ||
| 3 | import de.Hero.settings.Setting; | Aqua's settings framework โ slider/checkbox/mode GUI components |
| 4 | import events.Event; | Base event class โ all events extend this |
| 5 | import events.EventType; | Event phase enum (PRE/POST) |
| 6 | import events.listeners.EventPostRender2D; | Fires after 2D HUD rendering |
| 7 | import events.listeners.EventPreMotion; | Fires before a movement packet is sent โ where rotations are modified |
| 8 | import events.listeners.EventRender2D; | Fires during 2D HUD rendering โ used for block count display |
| 9 | import events.listeners.EventRenderNameTags; | Fires when nametags are rendered โ used for block ESP outline |
| 10 | import events.listeners.EventSycItem; | Fires to synchronize the client's held item slot โ scaffold intercepts this to force the block slot |
| 11 | import events.listeners.EventTick; | Fires every game tick (20/sec) โ used for offGround counter |
| 12 | import events.listeners.EventUpdate; | Fires every movement update โ core scaffold logic runs here |
| 13 | import intent.AquaDev.aqua.Aqua; | Aqua's main client class (singleton accessor) |
| 14 | import intent.AquaDev.aqua.gui.novolineOld.themesScreen.ThemeScreen; | Theme state for HUD rendering |
| 15 | import intent.AquaDev.aqua.modules.Category; | Module category enum (World, Combat, Movement, etc.) |
| 16 | import intent.AquaDev.aqua.modules.Module; | Base module class โ all Aqua modules extend this |
| 17 | import intent.AquaDev.aqua.modules.combat.Killaura; | Killaura module reference โ used for AutoDisable feature |
| 18 | import intent.AquaDev.aqua.modules.movement.Fly; | Fly module reference โ used for VerusFast tower's sendPacketUnlogged |
| 19 | import intent.AquaDev.aqua.modules.visual.Blur; | Blur shader utility for HUD rendering |
| 20 | import intent.AquaDev.aqua.modules.visual.ShaderMultiplier; | Glow shader utility |
| 21 | import intent.AquaDev.aqua.modules.visual.Shadow; | Shadow shader utility |
| 22 | import intent.AquaDev.aqua.utils.RandomUtil; | Random number utility โ used for tower randomization |
| 23 | import intent.AquaDev.aqua.utils.RenderUtil; | Rendering utilities โ rectangles, triangles, ESP blocks |
| 24 | import intent.AquaDev.aqua.utils.RotationUtil; | Rotation storage utility โ static yaw/pitch fields used by other modules |
| 25 | import intent.AquaDev.aqua.utils.TimeUtil; | Timer utility โ millisecond-based cooldown tracking |
| VANILLA MCP โ Standard Minecraft classes. These are from the deobfuscated Minecraft Coder Pack source. | ||
| 26 | import java.awt.Color; | Java AWT color for rendering |
| 27 | import java.util.ArrayList; | |
| 28 | import java.util.List; | |
| 29 | import net.minecraft.block.BlockAir; | BlockAir โ used to check if a position is empty (placeable) |
| 30 | import net.minecraft.block.BlockLiquid; | BlockLiquid โ scaffold avoids placing on liquid surfaces |
| 31 | import net.minecraft.block.material.Material; | Material enum โ used to check if a block is air |
| 32 | import net.minecraft.block.state.IBlockState; | Block state interface โ getBlockState() returns this |
| 33 | import net.minecraft.client.Minecraft; | The Minecraft singleton โ gateway to everything (mc.thePlayer, mc.theWorld, etc.) |
| 34 | import net.minecraft.client.gui.GuiNewChat; | Used to check if chat is open (animatedChatOpen) before rendering HUD |
| 35 | import net.minecraft.client.gui.ScaledResolution; | Screen resolution helper for HUD positioning |
| 36 | import net.minecraft.client.renderer.GlStateManager; | OpenGL state manager โ used for ESP rendering |
| 37 | import net.minecraft.init.Blocks; | Block registry access โ Blocks.air, Blocks.stone, etc. |
| 38 | import net.minecraft.init.Items; | Item registry โ Items.water_bucket used in VerusFast tower |
| 39 | import net.minecraft.item.Item; | Base Item class โ used for item ID lookups |
| 40 | import net.minecraft.item.ItemBlock; | ItemBlock โ items that are placable blocks (cobblestone, dirt, planks, etc.) |
| 41 | import net.minecraft.item.ItemStack; | Item stack โ count + metadata + NBT for a held item |
| 42 | import net.minecraft.network.play.client.C08PacketPlayerBlockPlacement; | C08 โ Block placement packet. This is THE critical packet for scaffold. |
| 43 | import net.minecraft.network.play.client.C0APacketAnimation; | C0A โ Arm swing animation packet. Used for silent swing. |
| 44 | import net.minecraft.potion.Potion; | Potion class โ used to check for moveSpeed potion for BMCBoost |
| 45 | import net.minecraft.util.AxisAlignedBB; | Bounding box โ used in rayTrace to check if a point is inside a block |
| 46 | import net.minecraft.util.BlockPos; | 3D coordinate (x, y, z) โ immutable, used everywhere for block positions |
| 47 | import net.minecraft.util.EnumFacing; | Direction enum (DOWN, UP, NORTH, SOUTH, WEST, EAST) โ which face of the block to place against |
| 48 | import net.minecraft.util.MathHelper; | Math utilities โ clamp_float, wrapAngleTo180_float, sqrt_double |
| 49 | import net.minecraft.util.MovingObjectPosition; | Ray-trace result โ what entity/block the player is looking at |
| 50 | import net.minecraft.util.Vec3; | 3D vector โ position math, ray origin/destination |
| 51 | import org.lwjgl.opengl.GL11; | OpenGL immediate mode โ used for ESP rendering |
Class Definition & FieldsLines 53-66
| Class declaration โ Scaffold extends Module (Aqua's client base module class) | ||
| 53 | public class Scaffold extends Module { | |
| 54 | private BlockPos espPos = null; | Position of the block below player โ used for ESP outline rendering |
| 55 | private final Scaffold.BlackList blackList = new Scaffold.BlackList(); | Blocks to avoid placing (slabs, chests, banners, etc.) |
| 56 | public float[] rots = new float[2]; | Stored rotation array [yaw, pitch] |
| 57 | public float[] lastRots = new float[2]; | Previous tick's rotations โ for smoothing |
| 58 | public int slot; | Currently selected hotbar slot (block slot) |
| 59 | public MovingObjectPosition objectPosition = null; | Ray-trace result from find() โ the block face the player will click |
| 60 | private final ArrayList<Vec3> lastPositions = new ArrayList<>(); | Historical position tracking (seems unused in this version) |
| 61 | private double[] xyz = new double[3]; | Coordinate storage (seems unused in this version) |
| 62 | public static Scaffold.BlockData data; | Static โ the current target block + face. Static because other modules read it. |
| 63 | TimeUtil timeUtil = new TimeUtil(); | Timer for tower mode cooldowns |
| 64 | private double posY; | Fixed Y-level for SameY mode โ scaffold places at this exact height |
| 65 | public boolean down; | Downward placement flag โ set when sneak key is held in Down mode |
| 66 | private int offGround = 0; | Ticks since player left the ground โ used for tower timing |
Constructor โ Settings RegistrationLines 68-91
| CLIENT BASE โ The constructor registers settings with Aqua's settings manager. Each Setting creates a GUI option the user can toggle/slide/select. | ||
| 68 | public Scaffold() { | |
| 69 | super("Scaffold", Module.Type.World, "Scaffold", 0, Category.World); | Module(name, type, displayName, keybind, category) |
| 70 | Aqua.setmgr.register(new Setting("Sprint", this, false)); | Toggle: auto-sprint while scaffolding |
| 71 | Aqua.setmgr.register(new Setting("EnableCalculate", this, false)); | Toggle: enable yaw/pitch calculation mode |
| 72 | Aqua.setmgr.register(new Setting("EnableSneak", this, false)); | Toggle: force-sneak while scaffolding |
| 73 | Aqua.setmgr.register(new Setting("BlockCount", this, true)); | Toggle: show block count HUD |
| 74 | Aqua.setmgr.register(new Setting("BlockESP", this, true)); | Toggle: highlight the block below player |
| 75 | Aqua.setmgr.register(new Setting("Intave", this, false)); | Toggle: Intave anti-cheat specific bypass |
| 76 | Aqua.setmgr.register(new Setting("Swing", this, false)); | Toggle: use client-side swingItem() vs silent C0A packet |
| 77 | Aqua.setmgr.register(new Setting("Down", this, false)); | Toggle: place blocks below when sneaking |
| 78 | Aqua.setmgr.register(new Setting("SameY", this, false)); | Toggle: place at a fixed Y level |
| 79 | Aqua.setmgr.register(new Setting("Expand", this, false)); | Toggle: extend placement reach in movement direction |
| 80 | Aqua.setmgr.register(new Setting("AutoDisable", this, false)); | Toggle: auto-disable scaffold when Killaura has a target |
| 81 | Aqua.setmgr.register(new Setting("BMCBoost", this, false)); | Toggle: increase game speed when speed potion is active |
| 82 | Aqua.setmgr.register(new Setting("LegitPlace", this, false)); | Toggle: use vanilla raytrace-based placement (slower but more legit) |
| 83 | Aqua.setmgr.register(new Setting("ReverseYaw", this, false)); | Toggle: reverse yaw calculation direction |
| 84 | Aqua.setmgr.register(new Setting("SneakModify", this, 1.0, 0.3, 1.0, false)); | Slider: sneak modification factor (0.3-1.0) |
| 85 | Aqua.setmgr.register(new Setting("Expandlength", this, 8.0, 0.0, 25.0, false)); | Slider: how far to extend placement (0-25 blocks) |
| 86 | Aqua.setmgr.register(new Setting("YawPosition", this, 90.0, 0.0, 380.0, false)); | Slider: yaw offset for calculated rotations (0-380) |
| 87 | Aqua.setmgr.register(new Setting("RotationMode", this, "Static", new String[]{"Static", "Calculated"})); | Mode: static (180 yaw, 82 pitch) or calculated (aim at block face) |
| 88 | Aqua.setmgr.register(new Setting("Shader", this, "Glow", new String[]{"Glow", "Shadow", "Jello"})); | Mode: HUD shader effect |
| 89 | Aqua.setmgr.register(new Setting("Tower", this, "None", new String[]{ | |
| 90 | "None", "Watchdog", "VerusFast", "IntaveFast", "Cubecraft", "UpdatedNCP", "WatchdogNew"})); | Mode: tower (auto-jump) bypass for specific anti-cheats |
| 91 | } | |
onEnable / onDisableLines 93-114
| onEnable โ called when the module is toggled ON | ||
| 93 | @Override | |
| 94 | public void onEnable() { | |
| 95 | if (Aqua.setmgr.getSetting("ScaffoldEnableCalculate").isState()) { | If calculate mode is on... |
| 96 | mc.thePlayer.rotationPitchHead = 82.0F; | Set client-side head pitch to 82ยฐ (looking straight down-ish) |
| 97 | mc.thePlayer.rotationYawHead = 180.0F; | Set head yaw to 180ยฐ (looking south, opposite of movement direction) |
| 98 | } | |
| 99 | ||
| 100 | this.offGround = 0; | Reset off-ground counter |
| 101 | if (Aqua.setmgr.getSetting("ScaffoldEnableSneak").isState()) { | If sneak mode is on... |
| 102 | mc.gameSettings.keyBindSneak.pressed = true; | Force the sneak key down (server sees player sneaking) |
| 103 | } | |
| 104 | ||
| 105 | this.posY = mc.thePlayer.posY; | Record current Y-level for SameY mode |
| 106 | super.onEnable(); | Call parent Module.onEnable() โ registers event listeners |
| 107 | } | |
| onDisable โ called when the module is toggled OFF | ||
| 109 | @Override | |
| 110 | public void onDisable() { | |
| 111 | mc.timer.timerSpeed = 1.0F; | Reset game speed (BMCBoost may have changed it) |
| 112 | mc.gameSettings.keyBindSneak.pressed = false; | Release forced sneak |
| 113 | super.onDisable(); | Call parent โ unregisters event listeners |
| 114 | } | |
onEvent โ EventTick: Off-Ground TrackingLines 117-156
| The main event handler. Aqua's event system calls onEvent() with every game event. The method dispatches based on event type. | ||
| 117 | @Override | |
| 118 | public void onEvent(Event event) { | |
| EventRenderNameTags โ Renders an ESP highlight on the block below the player. This is purely visual (CLIENT SIDE). | ||
| 118 | if (event instanceof EventRenderNameTags && Aqua.setmgr.getSetting("ScaffoldBlockESP").isState()) { | BlockESP setting must be enabled |
| 119 | BlockPos pos = new BlockPos(mc.thePlayer.posX, mc.thePlayer.posY - 1.0, mc.thePlayer.posZ); | Get block directly below the player |
| 120 | this.espPos = mc.theWorld.getBlockState(pos).getBlock().isFullBlock() ? pos : this.espPos; | Only highlight if it's a full block (not slabs, stairs, etc.) |
| 121 | if (this.espPos != null) { | If we have a position to highlight... |
| 122 | GL11.glEnable(3042); GL11.glBlendFunc(770, 771); | Enable blending (transparency) |
| 123 | GL11.glEnable(2848); GL11.glDisable(2929); GL11.glDisable(3553); | Enable smoothing, disable depth test, disable textures |
| 124 | GlStateManager.disableCull(); GL11.glDepthMask(false); | Disable face culling, allow rendering through blocks |
| 125 | Color color = new Color(Aqua.setmgr.getSetting("HUDColor").getColor()); | Get client theme color |
| 126 | Blur.drawBlurred(() -> RenderUtil.drawBlockESP(this.espPos, ...), false); | Draw blurred ESP behind the block |
| 127 | RenderUtil.drawBlockESP(this.espPos, ...); | Draw main ESP outline on top |
| 128 | GL11.glColor4f(1,1,1,1); GL11.glDepthMask(true); | Restore OpenGL state |
| 129 | GlStateManager.enableCull(); GL11.glEnable(3553); GL11.glEnable(2929); | Re-enable depth test, textures, face culling |
| 130 | } | |
| 131 | } | |
| EventTick โ Fires every game tick (20 times/sec). Simple counter: increment offGround when airborne, reset when on ground. | ||
| 150 | if (event instanceof EventTick) { | |
| 151 | if (mc.thePlayer.onGround) { | If player is touching the ground... |
| 152 | this.offGround = 0; | Reset counter |
| 153 | } else { | |
| 154 | ++this.offGround; | Increment โ tracks how many ticks since player left ground |
| 155 | } | |
| 156 | } | |
HUD Rendering โ EventRender2D / EventPostRender2DLines 158-338
| CLIENT BASE โ The entire HUD rendering section is pure visual. It draws the block count on screen using various shader effects (Glow, Shadow, Jello). No packets involved. | ||
| 158 | if (event instanceof EventRender2D && Aqua.setmgr.getSetting("ScaffoldBlockCount").isState()) { | Block count HUD display |
| 159-241 | /* ... HUD rendering with shaders ... */ | Renders "Blocks: count" text with Glow/Shadow/Jello shader effects at screen center. Three different rendering paths based on ThemeScreen state and Shader mode. |
| 242-338 | /* ... EventPostRender2D + second HUD path ... */ | Second HUD rendering path for non-Jello shader modes. Draws "Blocks : count" using comfortaa4 font. |
EventSycItem โ Silent Slot SwitchingLines 340-342
| EventSycItem โ Fires when the client synchronizes the held item slot. Scaffold overrides this to force the player's selected slot to a block. | ||
| 340 | if (event instanceof EventSycItem && this.getBlockSlot() != -1) { | If there's a block in the hotbar... |
| 341 | ((EventSycItem)event).slot = this.slot = this.getBlockSlot(); | Override the synced slot to the block slot. This is "silent" switching โ the client visually shows one item but the server thinks we're holding a block. |
| 342 | } | |
EventUpdate โ Core Scaffold Logic (PRE phase + main)Lines 344-539
| EventUpdate (PRE) โ First EventUpdate of the tick. Finds the target block and resets objectPosition. | ||
| 344 | if (event instanceof EventUpdate && event.type == EventType.PRE) { | PRE phase โ fires before movement is processed |
| 345 | this.objectPosition = null; | Clear previous frame's ray-trace result |
| 346 | data = this.find(new Vec3(0.0, 0.0, 0.0)); | KEY: Call find() to locate the next block to place on. Result stored in static `data` for other modules to read. |
| 347 | } | |
| EventUpdate (phase-independent) โ BMC Boost timer speed + AutoDisable | ||
| 349 | if (event instanceof EventUpdate) { | Runs on ALL EventUpdate phases |
| 350 | data = this.find(new Vec3(0.0, 0.0, 0.0)); | Re-find target block (belt-and-suspenders) |
| 351 | if (mc.thePlayer.getActivePotionEffect(Potion.moveSpeed) != null) { | If player has Speed potion... |
| 352 | if (Aqua.setmgr.getSetting("ScaffoldBMCBoost").isState()) { | If BMCBoost is enabled... |
| 353 | mc.timer.timerSpeed = 1.105F; | Speed up game timer by 10.5% โ player moves faster, scaffold places faster |
| 354 | } | |
| 355 | } else { | |
| 356 | mc.timer.timerSpeed = 1.0F; | Reset timer to normal speed |
| 357 | } | |
| 358 | ||
| 359 | if (Aqua.setmgr.getSetting("ScaffoldAutoDisable").isState() && Killaura.target != null) { | If AutoDisable is on AND Killaura has a target... |
| 360 | Aqua.moduleManager.getModuleByName("Scaffold").setState(false); | Auto-disable scaffold when fighting (prevents tower interference) |
| 361 | } | |
| TOWER MODES โ Each tower mode is a different bypass for jumping while scaffolding. They manipulate motionY to simulate jumping without triggering anti-cheat flags. | ||
| 365 | if (Aqua.setmgr.getSetting("ScaffoldTower").getCurrentMode().equalsIgnoreCase("Cubecraft") | Cubecraft tower โ uses 50ms timer gap + ground detection |
| 366 | && mc.gameSettings.keyBindJump.pressed) { | Only active when holding jump key |
| 367 | if (this.timeUtil.hasTimePassed(50L)) { mc.thePlayer.motionY = 0.42; this.timeUtil.reset(); } | Every 50ms, set motionY to 0.42 (vanilla jump height) |
| 368 | if (isOnGround(0.1)) { mc.thePlayer.motionY = 0.42; } | Also jump when close to ground (fast re-jump) |
| 369 | } | |
| 376 | if (Aqua.setmgr.getSetting("ScaffoldTower").getCurrentMode().equalsIgnoreCase("Watchdog")) { | Watchdog (Hypixel) tower โ uses ground + offGround randomization |
| 377 | if (mc.thePlayer.onGround) { mc.thePlayer.motionY = 0.409; } | Jump at slightly less than vanilla (0.409 vs 0.42) |
| 378 | if (this.offGround == 3) { | On the 3rd tick of being airborne... |
| 379 | float random1 = RandomUtil.instance.nextFloat(0.99089, 0.99099); | Add tiny random drag factor (0.9909 range) |
| 380 | mc.thePlayer.motionY = (mc.thePlayer.motionY - 0.1) * random1; | Reduce upward velocity slightly with randomness โ mimics natural jump variation |
| 381 | } | |
| 382 | } | |
| 387 | if (Aqua.setmgr.getSetting("ScaffoldTower").getCurrentMode().equalsIgnoreCase("UpdatedNCP")) { | Updated NCP tower โ different idling vs moving behavior |
| 388 | if (!mc.thePlayer.isMoving()) { | If standing still... |
| 389 | if (this.timeUtil.hasReached(2000L)) { this.timeUtil.reset(); } | Every 2 seconds, reset timer (anti-spam) |
| 390 | else if (mc.thePlayer.ticksExisted % 3 == 1 && this.offGround > 1 && !mc.thePlayer.onGround) { | Every 3 ticks while airborne... |
| 391 | mc.thePlayer.motionY = 0.4196; | Re-apply upward motion (almost vanilla jump height) |
| 392 | } this.timeUtil.reset(); | |
| 393 | } else { | If moving... |
| 394 | if (mc.thePlayer.onGround) { mc.thePlayer.motionY = 0.409; } | Same as Watchdog pattern |
| 395 | if (this.offGround == 3) { mc.thePlayer.motionY = (mc.thePlayer.motionY - 0.1) * random; } | Random drag at tick 3 |
| 396 | } | |
| 397 | } | |
| 408-418 | /* WatchdogNew tower โ same pattern as UpdatedNCP idling */ | Jump every 3 ticks while airborne with 2s cooldown |
| 420-431 | /* IntaveFast tower โ for Intave anti-cheat */ | Jump at 0.405, random drag at tick 5 (different timing) |
| 433-456 | /* VerusFast tower โ for Verus anti-cheat */ | Sends fake C08 water bucket + block placement packets to trigger Verus bypass. Uses Fly.sendPacketUnlogged() to send without event system notification. motionY = 0.70096 after 5s. |
| 435 | Fly.sendPacketUnlogged(new C08PacketPlayerBlockPlacement(...)); | C08 with water_bucket at (-1,-1,-1) โ fake block placement |
| SPRINT HANDLING โ Controls sprint state based on settings and anti-cheat mode. | ||
| 458-470 | /* Sprint logic */ | Three paths: normal (keyBindSprint), Intave mode (setSprinting), or disabled |
| BLOCK PLACEMENT โ The core "LegitPlace" vs "Direct" placement logic. | ||
| 472-539 | if (!Aqua.setmgr.getSetting("ScaffoldLegitPlace").isState()) { | DIRECT MODE โ Uses playerController.onPlayerRightClick() directly |
| 476-484 | /* Down mode โ shift Y-level when sneaking */ | When Down + sneak held: posY = player posY - 1 (place one block below) |
| 486 | data = this.find(new Vec3(0.0, 0.0, 0.0)); | Find target block again |
| 487 | if (data != null && this.getBlockSlot() != -1) { | If we have a target AND blocks to place... |
| 490 | Vec3 hitVec = new Vec3(Scaffold.BlockData.getPos()).addVector(0.5, 0.5, 0.5) | Calculate the hit vector: center of target block + offset in face direction |
| 491 | .add(new Vec3(Scaffold.BlockData.getFacing().getDirectionVec()).multi(0.5)); | Example: placing on TOP face of block at (10,64,10) โ hitVec = (10.5, 65.0, 10.5) |
| 493-502 | mc.playerController.onPlayerRightClick(mc.thePlayer, mc.theWorld, | PACKET TRIGGER: This MCP call sends C08PacketPlayerBlockPlacement to the server with the position, facing, and itemstack. This IS the block placement. |
| 503-507 | if (Swing) mc.thePlayer.swingItem(); else mc.getNetHandler().addToSendQueue(new C0APacketAnimation()); | C0A โ Silent swing: sends arm animation via packet (invisible to other players? No โ it still plays). Swing setting controls which method. |
| 512-539 | } else { | LEGIT MODE โ Uses vanilla right-click ray-trace + sneak automation |
| 515 | if (mc.theWorld.getBlockState(blockPos).getBlock() == Blocks.air) { | If block below player is air (no ground)... |
| 516 | mc.gameSettings.keyBindSneak.pressed = true; | Force sneak on (so player doesn't fall off edge) |
| 530 | if (mc.theWorld.getBlockState(blockPos).getBlock() == Blocks.air) { | If still no ground below... |
| 531 | this.rightClickMouse(mc.thePlayer.inventory.getStackInSlot(this.slot), this.slot); | Call custom rightClickMouse() which uses PlayerControllerMP to send C08 |
EventPreMotion โ Rotation OverrideLines 542-561
| EventPreMotion โ Fires RIGHT BEFORE the C03/C04/C05/C06 Player packet is sent. This is where scaffold overrides the player's look direction to ensure the block placement ray-trace hits the target. | ||
| 542 | if (event instanceof EventPreMotion) { | |
| 543 | if (data == null) return; | If no target block found, don't modify rotations |
| 544 | ||
| 545 | float[] rotation2 = rotationrecode2(data); | Calculate the target rotation (yaw/pitch) to look at the block face |
| 546 | if (Aqua.setmgr.getSetting("ScaffoldRotationMode").getCurrentMode().equalsIgnoreCase("Static")) { | Static mode: Always look at a fixed angle |
| 547 | RotationUtil.yaw = mc.thePlayer.rotationYawHead; | Store client-side head yaw for other modules |
| 548 | RotationUtil.pitch = mc.thePlayer.rotationPitchHead; | Store client-side head pitch |
| 549 | ((EventPreMotion)event).setYaw(mc.thePlayer.rotationYaw + 180.0F); | PACKET: Override yaw in the about-to-be-sent C03 to 180ยฐ from current. Player looks behind themselves. |
| 550 | ((EventPreMotion)event).setPitch(82.0F); | PACKET: Override pitch to 82ยฐ (nearly straight down). This ensures the block below the crosshair is targeted. |
| 551 | } | |
| 552 | ||
| 553 | if (Aqua.setmgr.getSetting("ScaffoldRotationMode").getCurrentMode().equalsIgnoreCase("Calculated")) { | Calculated mode: Aim precisely at the target block face |
| 554 | RotationUtil.yaw = mc.thePlayer.rotationYawHead; | Store head yaw |
| 555 | RotationUtil.pitch = mc.thePlayer.rotationPitchHead; | Store head pitch |
| 556 | ((EventPreMotion)event).setYaw(rotation2[0]); | PACKET: Override yaw to calculated value (aims at block face) |
| 557 | ((EventPreMotion)event).setPitch(rotation2[1]); | PACKET: Override pitch to calculated value (78-80ยฐ, slightly above straight down) |
Helper MethodsLines 564-658
| getPositionByFace(BlockPos, EnumFacing) โ Returns the center point of a specific block face. Used for ray-trace targeting. | ||
| 564 | private Vec3 getPositionByFace(BlockPos position, EnumFacing facing) { | |
| 565 | Vec3 offset = new Vec3(facing.getDirectionVec().getX() / 2.0, facing.getDirectionVec().getY() / 2.0, facing.getDirectionVec().getZ() / 2.0); | Half a block in the face direction. UP face โ (0, 0.5, 0). NORTH โ (0, 0, -0.5). |
| 566 | Vec3 point = new Vec3(position.getX() + 0.5, position.getY() + 0.5, position.getZ() + 0.5); | Center of the block |
| 567 | return point.add(offset); | Center of the specified face โ e.g., TOP face at (10,64,10) โ (10.5, 64.5, 10.5) |
| 568 | } | |
| rayTrace(Vec3, Vec3) โ Casts a ray from origin to position in 10 steps, checking if any solid block is hit. Used to verify the player can see the placement target. | ||
| 572 | private boolean rayTrace(Vec3 origin, Vec3 position) { | |
| 573 | Vec3 difference = position.subtract(origin); | Vector from origin to target |
| 574 | int steps = 10; | Ray resolution (10 check points) |
| 575 | double x = difference.xCoord / steps; | Step increment per axis |
| 576 | double y = difference.yCoord / steps; | |
| 577 | double z = difference.zCoord / steps; | |
| 578 | Vec3 point = origin; | Start at origin |
| 579 | ||
| 580 | for (int i = 0; i < steps; ++i) { | Step along the ray |
| 581 | BlockPos blockPosition = new BlockPos(point = point.addVector(x, y, z)); | Advance to next point and get block at that position |
| 582 | IBlockState blockState = Minecraft.getMinecraft().theWorld.getBlockState(blockPosition); | Get the state of the block at this position |
| 583 | if (!(blockState.getBlock() instanceof BlockLiquid) && !(blockState.getBlock() instanceof BlockAir)) { | If it's a solid block (not air, not liquid)... |
| 584 | AxisAlignedBB boundingBox = blockState.getBlock().getCollisionBoundingBox(world, blockPosition, blockState); | Get the collision bounding box of this block |
| 585 | if (boundingBox == null) boundingBox = new AxisAlignedBB(0, 0, 0, 0, 0, 0); | Blocks with no collision (like air) get empty AABB |
| 586 | if (boundingBox.offset(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()).isVecInside(point)) { | If the ray point is INSIDE this block's bounding box... |
| 587 | return true; | Ray hit something โ placement is blocked |
| 588 | } | |
| 589 | } | |
| 590 | } | |
| 591 | ||
| 592 | return false; | Ray reached destination without hitting anything โ clear line of sight |
| 593 | } | |
find() โ The Block-Finding AlgorithmLines 598-643
| find(Vec3) โ The heart of the scaffold. Determines WHICH block to place on and WHICH face to click. | ||
| 598 | private Scaffold.BlockData find(Vec3 offset3) { | |
| 599 | double xDiff = mc.thePlayer.posX - mc.thePlayer.prevPosX; | X velocity this tick |
| 600 | double zDiff = mc.thePlayer.posZ - mc.thePlayer.prevPosZ; | Z velocity this tick |
| 601 | float expand = (float)Aqua.setmgr.getSetting("ScaffoldExpandlength").getCurrentNumber(); | Expand distance multiplier (0-25 blocks) |
| 602 | double x = Expand ? mc.thePlayer.posX + xDiff * expand : mc.thePlayer.posX; | Extend X position in movement direction (predictive placement) |
| 603 | double y = !SameY && !Down ? mc.thePlayer.posY : (forward ? posY : mc.thePlayer.posY); | Y position: normal, SameY frozen, or Down shifted |
| 604 | double z = Expand ? mc.thePlayer.posZ + zDiff * expand : mc.thePlayer.posZ; | Extend Z position in movement direction |
| 605 | EnumFacing[] invert = {UP, DOWN, SOUTH, NORTH, EAST, WEST}; | Inverted face lookup โ if the adjacent block is NORTH, we place on the SOUTH face of it |
| 606 | BlockPos position = new BlockPos(x, y, z).offset(EnumFacing.DOWN); | Target: the block directly below the player's feet |
| PASS 1 โ Check all 6 faces of the target position for an adjacent solid block | ||
| 608 | for (EnumFacing facing : EnumFacing.values()) { | Try each direction (DOWN, UP, NORTH, SOUTH, WEST, EAST) |
| 609 | BlockPos offset = position.offset(facing); | Block adjacent to target in this direction |
| 610 | if (!(mc.theWorld.getBlockState(offset).getBlock() instanceof BlockAir) | If that adjacent block is not air (solid ground)... |
| 611 | && !this.rayTrace(mc.thePlayer.getLook(0.0F), this.getPositionByFace(offset, invert[facing.ordinal()]))) { | And we have clear line-of-sight to it (no blocks in the way)... |
| 612 | return new Scaffold.BlockData(invert[facing.ordinal()], offset); | FOUND: Place on this block using the inverted face (click the face pointing toward the target position) |
| 613 | } | |
| 614 | } | |
| PASS 2 โ Broader search: check positions offset horizontally from the target | ||
| 616 | BlockPos[] offsets = { | Search offsets: one block in each cardinal direction + two blocks out |
| 617 | new BlockPos(-1,0,0), new BlockPos(1,0,0), new BlockPos(0,0,-1), new BlockPos(0,0,1), | NSEW one block |
| 618 | new BlockPos(0,0,2), new BlockPos(0,0,-2), new BlockPos(2,0,0), new BlockPos(-2,0,0)}; | NSEW two blocks |
| 619 | for (BlockPos offset : offsets) { | For each horizontal offset... |
| 620 | BlockPos offsetPos = position.add(offset.getX(), 0, offset.getZ()); | Calculate offset position at same Y level |
| 621 | if (mc.theWorld.getBlockState(offsetPos).getBlock() instanceof BlockAir) { | If the offset position itself is empty (air)... |
| 622 | for (EnumFacing facing : EnumFacing.values()) { | Check each face of this air block |
| 623 | BlockPos offset2 = offsetPos.offset(facing); | Adjacent block to this air position |
| 624 | if (!(mc.theWorld.getBlockState(offset2).getBlock() instanceof BlockAir) | If that adjacent block is solid... |
| 625 | && !this.rayTrace(mc.thePlayer.getLook(0.01F), this.getPositionByFace(offset, invert[facing.ordinal()]))) { | And has clear line-of-sight... |
| 626 | return new Scaffold.BlockData(invert[facing.ordinal()], offset2); | FOUND: Place on offset2 clicking the face toward the air gap |
| 627 | } | |
| 628 | } | |
| 629 | } | |
| 630 | } | |
| 631 | return null; | No valid placement found โ can't scaffold here |
| 632 | } | |
getBlockSlot, rotationrecode2, rightClickMouse, getBlockCountLines 645-758
| getBlockSlot() โ Scans hotbar (slots 0-8) for a block item that isn't blacklisted. Returns slot index or -1. | ||
| 645 | public int getBlockSlot() { | |
| 646 | for (int i = 0; i < 9; ++i) { | Iterate hotbar slots 0-8 |
| 647 | ItemStack s = mc.thePlayer.inventory.getStackInSlot(i); | Get item in this slot |
| 648 | if (s != null && s.getItem() instanceof ItemBlock) { | If it's a placeable block item... |
| 649 | if (var10000.isNotBlackListed(Item.itemRegistry.getNameForObject(s.getItem()).toString())) { | And it's not blacklisted (not a slab/chest/banner etc.)... |
| 650 | return i; | Return this slot index |
| 651 | } | |
| 652 | } | |
| 653 | } | |
| 654 | return -1; | No blocks found in hotbar |
| 655 | } | |
| rotationrecode2(BlockData) โ Calculates the precise yaw/pitch needed to aim at the target block face. Accounts for mouse sensitivity to avoid "illegal" rotation jumps. | ||
| 681 | public static float[] rotationrecode2(Scaffold.BlockData blockData) { | |
| 682 | double x = BlockData.getPos().getX() + 0.5 - mc.thePlayer.posX + getFacing().getFrontOffsetX() / 2.0; | X offset from player to target face center |
| 683 | double z = BlockData.getPos().getZ() + 0.5 - mc.thePlayer.posZ + getFacing().getFrontOffsetZ() / 2.0; | Z offset from player to target face center |
| 684 | double y = BlockData.getPos().getY() + 0.6; | Target Y = block bottom + 0.6 (slightly above center) |
| 685 | double ymax = mc.thePlayer.posY + mc.thePlayer.getEyeHeight() - y; | Vertical distance from player's eyes to target |
| 686 | double allmax = MathHelper.sqrt_double(x * x + z * z); | Horizontal distance from player to target |
| 687 | float yaw = ReverseYaw ? (float)(Math.atan2(z, x) * 180.0 / Math.PI) - 270.0F | ReverseYaw: atan2 returns angle in radians โ convert to degrees, offset by 270 |
| 688 | : (float)(Math.atan2(z, x) * 180.0 / Math.PI) - YawPosition; | Normal: atan2(z,x)*180/ฯ - YawPosition slider value (default 90ยฐ) |
| 689 | float pitch = (float)(Math.atan2(ymax, allmax) * 180.0 / Math.PI); | Pitch = angle from horizontal to target. atan2(vertical, horizontal) โ degrees |
| 690 | if (yaw < 0.0F) yaw += 360.0F; | Normalize yaw to 0-360ยฐ |
| 691-694 | // Mouse sensitivity granularity fix | MCP REF: Minecraft applies mouse sensitivity as a multiplier to rotation changes. Without this fix, tiny rotation differences get rounded to 0 and the server rejects the packet (IllegalArgumentException). This ensures rotations snap to valid sensitivity increments. |
| 697 | return new float[]{yaw, MathHelper.clamp_float(pitch, 78.0F, 80.0F)}; | PACKET: Pitch clamped to 78-80ยฐ (slightly above straight-down). This narrow range ensures the rotation change in the C03 packet is valid. |
| rightClickMouse(ItemStack, int) โ Fallback placement method for LegitPlace mode. Simulates a vanilla right-click using PlayerControllerMP. | ||
| 702-727 | public void rightClickMouse(ItemStack itemstack, int slot) { | MCP REF: Mirrors Minecraft's rightClickMouse() but sends C0A packet instead of swingItem() when Swing setting is off. Uses PlayerControllerMP.onPlayerRightClick() which sends C08. |
| isOnGround(double) โ Checks if the player is within `height` blocks of the ground using collision detection. | ||
| 756 | public static boolean isOnGround(double height) { | |
| 757 | return !mc.theWorld.getCollidingBoundingBoxes(mc.thePlayer, mc.thePlayer.getEntityBoundingBox().offset(0.0, -height, 0.0)).isEmpty(); | Expand player's bounding box downward by `height` and check for collisions |
| 758 | } | |
Inner Classes โ BlackList & BlockDataLines 760-811
| BlackList โ Prevents the scaffold from placing certain problematic blocks. Slabs would create half-slabs (not full ground), chests/furnaces/beacons have GUI interactions, banners/saplings/web are non-solid or decorative. | ||
| 760 | class BlackList { | |
| 761 | List<String> stringids = new ArrayList<>(); | List of blacklisted item registry names |
| 763 | public BlackList() { | |
| 764-779 | // Adds IDs: wooden_slab, stone_slab, banner, beacon, trapped_chest, chest, | Blacklisted: slabs (would make half-slabs), interactive blocks (chest, furnace, anvil, enchanting table, crafting table, beacon), non-full blocks (web, sapling, banner) |
| 785 | public boolean isNotBlackListed(String blockID) { | |
| 786 | return !this.stringids.contains(blockID); | Returns true if the block is safe to place |
| 787 | } | |
| 788 | } | |
| BlockData โ Simple struct holding the placement target (which block to click, which face of it to click). Static fields because other modules read Scaffold.data directly. | ||
| 794 | public static class BlockData { | Static inner class โ accessible from other modules without a Scaffold instance |
| 795-810 | private static EnumFacing facing; private static BlockPos pos; | The face to click and the block position. Static because they represent the singleton scaffold target for this tick. |
Packet Interaction Summary
- C08PacketPlayerBlockPlacement โ Sent indirectly via
PlayerControllerMP.onPlayerRightClick(). Carries position, face direction, held item, and cursor coordinates. This is THE packet that places the block. - C0APacketAnimation โ Sent explicitly via
mc.getNetHandler().addToSendQueue(new C0APacketAnimation()). Tells the server to play the arm swing animation. Used instead ofswingItem()when "Swing" setting is off (silent swing). - C03PacketPlayer (C04/C05/C06) โ Modified via EventPreMotion yaw/pitch override. The scaffold overrides rotation to 180ยฐ yaw / 82ยฐ pitch (Static) or calculated block face angles (Calculated). Tower modes also modify motionY which gets sent in the next C04/C05/C06 position packet.
- C09PacketHeldItemChange โ Handled via EventSycItem. The scaffold forces the held item slot to a block slot silently, so the C08 placement packet uses the correct item.
- C0BPacketEntityAction โ Implicitly triggered via
keyBindSneak.pressed = true. The vanilla game loop sends the START_SNEAKING action when the sneak key is held.
CLIENT BASE NOTE โ The event system (Event, EventUpdate, EventPreMotion, EventTick, EventSycItem, EventRender2D, etc.) is part of Aqua's client framework, not Minecraft's code. Events are dispatched by Aqua's Module base class. The scaffold registers to receive these events by calling
super.onEnable() which hooks into Aqua's event bus. The settings framework (Aqua.setmgr, Setting) is also Aqua-specific. The rotation storage (RotationUtil) is shared state used by other modules like KillAura to know what the scaffold is doing.
VANILLA MCP REFERENCE โ All
net.minecraft.* classes are from the Minecraft Coder Pack (MCP) deobfuscated source for Minecraft 1.8.9. The PlayerControllerMP.onPlayerRightClick() method at net.minecraft.client.multiplayer.PlayerControllerMP:78 is called by Minecraft.rightClickMouse() in the vanilla game. The scaffold calls it directly to trigger the C08 packet path without requiring the player to physically right-click.