Class BlockPlacementManager

java.lang.Object
me.hackware.api.BlockPlacementManager

public class BlockPlacementManager extends Object
Centralized block-placement manager. Handles placement rate-limiting, per-block cooldowns, air-place, rotation, offhand swap tricks, and placement sounds.

Configuration is read through AntiCheatBridge.PlacementPolicy. Ported from Meteor's BlockPlacementManager, adapted to Hackware APIs and Mojang mappings.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static final record 
     
  • Method Summary

    Modifier and Type
    Method
    Description
    boolean
    beginPlacement(List<net.minecraft.core.BlockPos> positions, net.minecraft.world.item.Item item)
    Begins a placement transaction with a list of positions.
    boolean
    beginPlacement(List<net.minecraft.core.BlockPos> positions, net.minecraft.world.item.Item item, boolean asymmetric)
    As beginPlacement(List, Item); with asymmetric an item swapped up from the deep inventory is left in the hotbar afterwards (see SwapManager's asymmetric swap), so a caller placing the same item again and again pays the container swap once.
    boolean
    beginPlacement(net.minecraft.core.BlockPos position, net.minecraft.world.level.block.state.BlockState state, net.minecraft.world.item.Item item)
    Begins a placement transaction.
    boolean
    beginPlacement(net.minecraft.core.BlockPos position, net.minecraft.world.level.block.state.BlockState state, net.minecraft.world.item.Item item, boolean asymmetric)
    As beginPlacement(BlockPos, BlockState, Item); with asymmetric an item swapped up from the deep inventory is left in the hotbar afterwards (see SwapManager's asymmetric swap), so a caller placing the same item again and again pays the container swap once.
    boolean
    beginPlacement(net.minecraft.core.BlockPos position, net.minecraft.world.level.block.state.BlockState state, net.minecraft.world.item.Item item, boolean asymmetric, boolean keepSelected)
    As beginPlacement(BlockPos, BlockState, Item, boolean); with keepSelected the item is really selected in the hotbar (promoted there first when deep) and left selected after endPlacement() (SwapManager.beginSwap(Item, boolean, boolean, boolean)).
    boolean
    canBeginPlacement(List<net.minecraft.core.BlockPos> positions, net.minecraft.world.item.Item item)
    Whether beginPlacement(List, Item) would open a transaction right now: interact budget left, none open, the item in the inventory and a position placeable now, an insta-breakable one counting when SilentMine's burst would clear it this tick.
    static boolean
    canPlaceOver(net.minecraft.core.BlockPos pos)
    Returns true if a block can be placed at this position (either the block is replaceable, or it's an insta-breakable block that BPM will clear with a break packet before placing).
    boolean
    checkPlacement(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos)
     
    boolean
    checkPlacement(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, net.minecraft.world.level.block.state.BlockState state)
     
    boolean
    checkPlacement(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, net.minecraft.world.level.block.state.BlockState state, net.minecraft.world.level.block.state.BlockState desiredState)
     
    void
     
    void
    forceResetPlaceCooldown(net.minecraft.core.BlockPos blockPos)
     
    get()
     
    int
    Returns packets sent in the current rate-limit window (0 when idle/reset).
    static net.minecraft.core.Direction
    getPlaceOnDirection(net.minecraft.core.BlockPos pos)
     
    int
    Returns how many placement packets can still be sent in the current rate-limit window.
    long
    Returns the timestamp the current rate-limit window started, or -1 if idle.
    void
    Register event handlers.
    static boolean
    isInstaBreakAndShouldClear(net.minecraft.world.level.block.state.BlockState state, net.minecraft.core.BlockPos blockPos)
    Returns true if the given non-replaceable block can be insta-broken (zero hardness) and therefore should be cleared with a break packet immediately before placement.
    boolean
    isOnPlaceCooldown(net.minecraft.core.BlockPos blockPos)
    Returns true if the position is still on per-block retry cooldown.
    int
    placeableCount(net.minecraft.world.item.Item item)
    How many places of the item can be sent now: the items left (stackLeft(int, net.minecraft.world.item.Item)) in the stacks findPlaceStack(net.minecraft.world.item.Item) takes them from.
    boolean
    placeBlock(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos)
    Places a single block.
    boolean
    placeBlock(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, boolean ignorePerBlockCooldown)
    Placement variant that can bypass the per-position retry cooldown (AntiCheatBridge.PlacementPolicy.perBlockCooldownMs()).
    boolean
    placeBlock(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, net.minecraft.world.level.block.state.BlockState state)
     
    boolean
    placeBlock(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, net.minecraft.world.level.block.state.BlockState state, boolean ignorePerBlockCooldown)
     
    List<net.minecraft.core.BlockPos>
    placeBlocksBatch(List<net.minecraft.core.BlockPos> blocks, net.minecraft.world.item.Item item)
    Batch placement — keeps the swap open and sends multiple placement packets.
    List<net.minecraft.core.BlockPos>
    placeBlocksBatch(List<net.minecraft.core.BlockPos> blocks, net.minecraft.world.item.Item item, boolean asymmetric)
    Batch placement variant.
    List<net.minecraft.core.BlockPos>
    placeBlocksBatchWithState(List<BlockPlacementManager.PlacementRequest> blocks, net.minecraft.world.item.Item item)
    Batch placement with desired block states (for oriented slabs, stairs, etc.).
    List<net.minecraft.core.BlockPos>
    placeBlocksBatchWithState(List<BlockPlacementManager.PlacementRequest> blocks, net.minecraft.world.item.Item item, boolean asymmetric)
    Batch placement with desired block states (for oriented slabs, stairs, etc.).
    List<net.minecraft.core.BlockPos>
    placeBlocksBatchWithState(List<BlockPlacementManager.PlacementRequest> blocks, net.minecraft.world.item.Item item, boolean asymmetric, boolean sneakOnContainers)
    Batch placement with desired block states (for oriented slabs, stairs, etc.).
    List<net.minecraft.core.BlockPos>
    placeBlocksBatchWithState(List<BlockPlacementManager.PlacementRequest> blocks, net.minecraft.world.item.Item item, boolean asymmetric, boolean sneakOnContainers, boolean keepSelected)
    As placeBlocksBatchWithState(List, Item, boolean, boolean); with keepSelected the item is really selected in the hotbar (promoted there first when deep) and left selected afterwards, for the next batch of it (SwapManager.beginSwap(Item, boolean, boolean, boolean)).
    void
    removePendingSound(net.minecraft.core.BlockPos pos)
    Remove a pending sound entry so the client-side sound is not played when the server confirms the block update (avoids double sounds).

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • get

      public static BlockPlacementManager get()
    • init

      public void init()
      Register event handlers. Call once during mod init.
    • beginPlacement

      public boolean beginPlacement(net.minecraft.core.BlockPos position, net.minecraft.world.level.block.state.BlockState state, net.minecraft.world.item.Item item)
      Begins a placement transaction. Swaps the item into the main hand. Must be followed by placeBlock(net.minecraft.world.item.Item, net.minecraft.core.BlockPos) calls and then endPlacement().
    • beginPlacement

      public boolean beginPlacement(net.minecraft.core.BlockPos position, net.minecraft.world.level.block.state.BlockState state, net.minecraft.world.item.Item item, boolean asymmetric)
      As beginPlacement(BlockPos, BlockState, Item); with asymmetric an item swapped up from the deep inventory is left in the hotbar afterwards (see SwapManager's asymmetric swap), so a caller placing the same item again and again pays the container swap once.
    • beginPlacement

      public boolean beginPlacement(net.minecraft.core.BlockPos position, net.minecraft.world.level.block.state.BlockState state, net.minecraft.world.item.Item item, boolean asymmetric, boolean keepSelected)
      As beginPlacement(BlockPos, BlockState, Item, boolean); with keepSelected the item is really selected in the hotbar (promoted there first when deep) and left selected after endPlacement() (SwapManager.beginSwap(Item, boolean, boolean, boolean)).
    • beginPlacement

      public boolean beginPlacement(List<net.minecraft.core.BlockPos> positions, net.minecraft.world.item.Item item)
      Begins a placement transaction with a list of positions. Succeeds if at least one position is valid for placement.
    • beginPlacement

      public boolean beginPlacement(List<net.minecraft.core.BlockPos> positions, net.minecraft.world.item.Item item, boolean asymmetric)
      As beginPlacement(List, Item); with asymmetric an item swapped up from the deep inventory is left in the hotbar afterwards (see SwapManager's asymmetric swap), so a caller placing the same item again and again pays the container swap once.
    • canBeginPlacement

      public boolean canBeginPlacement(List<net.minecraft.core.BlockPos> positions, net.minecraft.world.item.Item item)
      Whether beginPlacement(List, Item) would open a transaction right now: interact budget left, none open, the item in the inventory and a position placeable now, an insta-breakable one counting when SilentMine's burst would clear it this tick. Side-effect free (nothing is mined, and a clear that would defer is not requested), for a caller that has to free the hands before it places, as Surround ends an AutoEat chew.
    • placeBlock

      public boolean placeBlock(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos)
      Places a single block. Must be called between beginPlacement(net.minecraft.core.BlockPos, net.minecraft.world.level.block.state.BlockState, net.minecraft.world.item.Item) and endPlacement(), or standalone (will use SwapManager internally).
    • placeBlock

      public boolean placeBlock(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, boolean ignorePerBlockCooldown)
      Placement variant that can bypass the per-position retry cooldown (AntiCheatBridge.PlacementPolicy.perBlockCooldownMs()). Used by Surround so it isn't throttled re-placing the same slot; all other modules keep the retry delay.
    • placeBlock

      public boolean placeBlock(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, net.minecraft.world.level.block.state.BlockState state)
    • placeBlock

      public boolean placeBlock(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, net.minecraft.world.level.block.state.BlockState state, boolean ignorePerBlockCooldown)
    • placeBlocksBatch

      public List<net.minecraft.core.BlockPos> placeBlocksBatch(List<net.minecraft.core.BlockPos> blocks, net.minecraft.world.item.Item item)
      Batch placement — keeps the swap open and sends multiple placement packets. Returns the positions that were actually placed.
    • placeBlocksBatch

      public List<net.minecraft.core.BlockPos> placeBlocksBatch(List<net.minecraft.core.BlockPos> blocks, net.minecraft.world.item.Item item, boolean asymmetric)
      Batch placement variant.
      Parameters:
      asymmetric - when true, a block item that has to be silent-swapped up from the deep inventory is left in the hotbar afterward instead of being swapped back (see SwapManager.beginSwap(Item, boolean, boolean)). Continuous builders (Scaffold, HighwayCleaner) set this so they promote the block into the hotbar once and reuse it, instead of paying a swap-back every tick.
    • placeBlocksBatchWithState

      public List<net.minecraft.core.BlockPos> placeBlocksBatchWithState(List<BlockPlacementManager.PlacementRequest> blocks, net.minecraft.world.item.Item item)
      Batch placement with desired block states (for oriented slabs, stairs, etc.).
    • placeBlocksBatchWithState

      public List<net.minecraft.core.BlockPos> placeBlocksBatchWithState(List<BlockPlacementManager.PlacementRequest> blocks, net.minecraft.world.item.Item item, boolean asymmetric)
      Batch placement with desired block states (for oriented slabs, stairs, etc.).
      Parameters:
      asymmetric - when true, an item that has to be silent-swapped up from the deep inventory is left in the hotbar afterward instead of being swapped back (see SwapManager.beginSwap(Item, boolean, boolean)).
    • placeBlocksBatchWithState

      public List<net.minecraft.core.BlockPos> placeBlocksBatchWithState(List<BlockPlacementManager.PlacementRequest> blocks, net.minecraft.world.item.Item item, boolean asymmetric, boolean sneakOnContainers)
      Batch placement with desired block states (for oriented slabs, stairs, etc.).
      Parameters:
      asymmetric - see placeBlocksBatchWithState(List, Item, boolean)
      sneakOnContainers - when true, a placement whose clicked face belongs to a block that opens a menu (chest, barrel, furnace, crafting table, …) is wrapped in sneak/unsneak input packets so the server places instead of opening the GUI
    • placeBlocksBatchWithState

      public List<net.minecraft.core.BlockPos> placeBlocksBatchWithState(List<BlockPlacementManager.PlacementRequest> blocks, net.minecraft.world.item.Item item, boolean asymmetric, boolean sneakOnContainers, boolean keepSelected)
      As placeBlocksBatchWithState(List, Item, boolean, boolean); with keepSelected the item is really selected in the hotbar (promoted there first when deep) and left selected afterwards, for the next batch of it (SwapManager.beginSwap(Item, boolean, boolean, boolean)).
    • placeableCount

      public int placeableCount(net.minecraft.world.item.Item item)
      How many places of the item can be sent now: the items left (stackLeft(int, net.minecraft.world.item.Item)) in the stacks findPlaceStack(net.minecraft.world.item.Item) takes them from.
    • endPlacement

      public void endPlacement()
    • forceResetPlaceCooldown

      public void forceResetPlaceCooldown(net.minecraft.core.BlockPos blockPos)
    • isOnPlaceCooldown

      public boolean isOnPlaceCooldown(net.minecraft.core.BlockPos blockPos)
      Returns true if the position is still on per-block retry cooldown.
    • getRemainingBudget

      public int getRemainingBudget()
      Returns how many placement packets can still be sent in the current rate-limit window.
    • getPacketsSent

      public int getPacketsSent()
      Returns packets sent in the current rate-limit window (0 when idle/reset).
    • getWindowStartTimestamp

      public long getWindowStartTimestamp()
      Returns the timestamp the current rate-limit window started, or -1 if idle.
    • checkPlacement

      public boolean checkPlacement(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos)
    • checkPlacement

      public boolean checkPlacement(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, net.minecraft.world.level.block.state.BlockState state)
    • isInstaBreakAndShouldClear

      public static boolean isInstaBreakAndShouldClear(net.minecraft.world.level.block.state.BlockState state, net.minecraft.core.BlockPos blockPos)
      Returns true if the given non-replaceable block can be insta-broken (zero hardness) and therefore should be cleared with a break packet immediately before placement. Used to allow placing onto things like tnt, scaffolding, torches, redstone, etc. without performing an extra inventory swap.
    • canPlaceOver

      public static boolean canPlaceOver(net.minecraft.core.BlockPos pos)
      Returns true if a block can be placed at this position (either the block is replaceable, or it's an insta-breakable block that BPM will clear with a break packet before placing). Use this from modules (Surround, AutoTrap, etc.) instead of a raw canBeReplaced() check so insta-breakable blocks like tnt/scaffolding/torches are not skipped.
    • checkPlacement

      public boolean checkPlacement(net.minecraft.world.item.Item item, net.minecraft.core.BlockPos blockPos, net.minecraft.world.level.block.state.BlockState state, net.minecraft.world.level.block.state.BlockState desiredState)
    • getPlaceOnDirection

      public static net.minecraft.core.Direction getPlaceOnDirection(net.minecraft.core.BlockPos pos)
    • removePendingSound

      public void removePendingSound(net.minecraft.core.BlockPos pos)
      Remove a pending sound entry so the client-side sound is not played when the server confirms the block update (avoids double sounds).