Skip to content
Samuel De Oliveira edited this page Jan 12, 2026 · 1 revision

📦 Cuboid — Core

Manipulate 3D rectangular regions with advanced block and entity operations.


Overview

The Cuboid API provides three types of cuboids for different use cases:

  • Cuboid — Basic rectangular region manipulation
  • MovableCuboid — Move regions with blocks and entities
  • MemoryCuboid — Save and restore cuboid states

Each type offers specialized methods for block operations, entity detection, and animations.


Table of Contents


Cuboid Types

Choosing the Right Type

Type Use Case Key Features
Cuboid Static regions Block operations, entity detection, animations
MovableCuboid Dynamic regions Move with blocks/entities, shift by vector
MemoryCuboid State management Save/restore, arena resets, rollback

Quick Comparison

// Basic Cuboid - Static operations
Cuboid basic = new Cuboid(loc1, loc2);
basic.setMaterial(Material.STONE);

// MovableCuboid - Dynamic movement
MovableCuboid movable = new MovableCuboid(loc1, loc2);
movable.move(newCenter, true, true);

// MemoryCuboid - State preservation
MemoryCuboid memory = new MemoryCuboid(loc1, loc2);
memory.restoreAll();

Basic Cuboid

Creation

Location corner1 = new Location(world, 0, 64, 0);
Location corner2 = new Location(world, 10, 74, 10);

Cuboid cuboid = new Cuboid(corner1, corner2);

Location Methods

Get Center

// Get 3D center (middle of X, Y, Z)
Location center = cuboid.getCenter();

player.teleport(center);

Get XZ Center

// Get center on XZ plane (Y at lowest point + 1)
Location centerXZ = cuboid.getCenterXZ();

// Useful for ground-level teleports
player.teleport(centerXZ);

Block Operations

Get All Blocks

List<Block> blocks = cuboid.getBlocks();

player.sendMessage(
  Component.text("Total blocks: " + blocks.size(), NamedTextColor.GOLD)
);

for (Block block : blocks) {
  if (block.getType() == Material.DIAMOND_ORE) {
    player.sendMessage(
      Component.text("Found diamond at: " + block.getLocation(), NamedTextColor.AQUA)
    );
  }
}

Set Material

// Fill entire cuboid with stone
cuboid.setMaterial(Material.STONE);

// Fill with air (clear region)
cuboid.setMaterial(Material.AIR);

// Fill with glass
cuboid.setMaterial(Material.GLASS);

Set BlockData

// Create BlockData
BlockData glassData = Bukkit.createBlockData(Material.BLUE_STAINED_GLASS);

// Fill with BlockData
cuboid.setBlockData(glassData);

// With properties
BlockData stairs = Bukkit.createBlockData(Material.OAK_STAIRS, "[facing=north,half=bottom]");
cuboid.setBlockData(stairs);

Replace Material

// Replace all dirt with grass
cuboid.replaceMaterial(Material.DIRT, Material.GRASS_BLOCK);

// Replace stone with diamond blocks
cuboid.replaceMaterial(Material.STONE, Material.DIAMOND_BLOCK);

// Remove water
cuboid.replaceMaterial(Material.WATER, Material.AIR);

Replace BlockData

BlockData oldData = Bukkit.createBlockData(Material.STONE);
BlockData newData = Bukkit.createBlockData(Material.GLOWSTONE);

cuboid.replaceBlockData(oldData, newData);

Count Blocks

// Count specific material
int diamondCount = cuboid.countBlocksOfMaterial(Material.DIAMOND_ORE);
int goldCount = cuboid.countBlocksOfMaterial(Material.GOLD_ORE);

player.sendMessage(
  Component.text("Diamond ore: " + diamondCount, NamedTextColor.AQUA)
);
player.sendMessage(
  Component.text("Gold ore: " + goldCount, NamedTextColor.GOLD)
);

// Count BlockData
BlockData glassData = Bukkit.createBlockData(Material.GLASS);
int glassCount = cuboid.countBlocksOfBlockData(glassData);

Animated Operations

Animate Fill

// Fill from bottom to top (5 ticks per layer)
cuboid.setMaterialEveryTick(Material.GLASS, 5L, false);

// Fill from top to bottom (10 ticks per layer)
cuboid.setMaterialEveryTick(Material.STONE, 10L, true);

// Fast animation (1 tick per layer)
cuboid.setMaterialEveryTick(Material.WATER, 1L, false);

Animate with BlockData

BlockData data = Bukkit.createBlockData(Material.BLUE_STAINED_GLASS);

// Bottom to top
cuboid.setBlockDataEveryTick(data, 5L, false);

// Top to bottom
cuboid.setBlockDataEveryTick(data, 5L, true);

Animate Replacement

// Replace dirt with grass from bottom to top
cuboid.replaceMaterialEveryTick(
  Material.DIRT, 
  Material.GRASS_BLOCK, 
  5L, 
  false
);

// Replace stone with glowstone from top to bottom
cuboid.replaceMaterialEveryTick(
  Material.STONE, 
  Material.GLOWSTONE, 
  10L, 
  true
);

Animate BlockData Replacement

BlockData oldData = Bukkit.createBlockData(Material.STONE);
BlockData newData = Bukkit.createBlockData(Material.DIAMOND_BLOCK);

cuboid.replaceBlockDataEveryTick(oldData, newData, 5L, false);

Entity Detection

Get All Entities

// Default boundary extension (1, 1, 1)
List<Entity> entities = cuboid.getEntities();

for (Entity entity : entities) {
  if (entity instanceof Player player) {
    player.sendMessage(
      Component.text("You are in the cuboid!", NamedTextColor.GREEN)
    );
  }
}

With Boundary Extension

// Extend boundary by 5 blocks on all axes
List<Entity> extended = cuboid.getEntities(5.0);

// Custom boundary extension (X, Y, Z)
Vector extension = new Vector(10, 2, 10);
List<Entity> custom = cuboid.getEntities(extension);

Filter Entities

List<Entity> entities = cuboid.getEntities();

// Count players
long playerCount = entities.stream()
  .filter(e -> e instanceof Player)
  .count();

// Get hostile mobs
List<Monster> monsters = entities.stream()
  .filter(e -> e instanceof Monster)
  .map(e -> (Monster) e)
  .toList();

player.sendMessage(
  Component.text("Players: " + playerCount, NamedTextColor.GREEN)
);
player.sendMessage(
  Component.text("Monsters: " + monsters.size(), NamedTextColor.RED)
);

Location Checking

Basic Check

if (cuboid.isLocationIn(player.getLocation())) {
  player.sendMessage(
    Component.text("You are inside!", NamedTextColor.GREEN)
  );
} else {
  player.sendMessage(
    Component.text("You are outside!", NamedTextColor.RED)
  );
}

With Custom Boundary

Vector boundary = new Vector(5, 0, 5);

if (cuboid.isLocationIn(location, boundary)) {
  getLogger().info("Location is inside with extended boundary");
}

Visual Debugging

Show Full Cuboid

// Show all blocks within 30 block radius
cuboid.show(player, 30.0, false, new Vector(1, 1, 1));

Show Borders Only

// Show only the edges (more performant)
cuboid.show(player, 50.0, true, new Vector(1, 1, 1));

With Extended Boundaries

// Show with 2 block extension on X and Z
Vector extension = new Vector(2, 0, 2);
cuboid.show(player, 40.0, true, extension);

MovableCuboid

MovableCuboid extends Cuboid with movement capabilities.

Creation

Location corner1 = new Location(world, 0, 64, 0);
Location corner2 = new Location(world, 10, 74, 10);

MovableCuboid movable = new MovableCuboid(corner1, corner2);

Move to Center

Location newCenter = new Location(world, 50, 64, 50);

// Move only the cuboid boundaries
movable.move(newCenter, false, false);

// Move cuboid with entities (players, mobs)
movable.move(newCenter, true, false);

// Move cuboid with blocks
movable.move(newCenter, false, true);

// Move everything (boundaries + entities + blocks)
movable.move(newCenter, true, true);

Shift by Vector

// Move 10 blocks east, 5 blocks up
Vector offset = new Vector(10, 5, 0);

// Move only boundaries
movable.add(offset, false, false);

// Move with entities
movable.add(offset, true, false);

// Move with blocks
movable.add(offset, false, true);

// Move everything
movable.add(offset, true, true);

Example: Moving Platform

public class MovingPlatform {

  private MovableCuboid platform;

  public void createPlatform(Location start) {
    Location corner1 = start.clone();
    Location corner2 = start.clone().add(10, 1, 10);

    platform = new MovableCuboid(corner1, corner2);

    // Build platform
    platform.setMaterial(Material.STONE);
  }

  public void movePlatform(Vector direction) {
    // Move platform with players on it
    platform.add(direction, true, true);

    // Notify passengers
    for (Entity entity : platform.getEntities()) {
      if (entity instanceof Player player) {
        player.sendMessage(
          Component.text("Platform moving!", NamedTextColor.GOLD)
        );
        player.playSound(
          player.getLocation(),
          Sound.BLOCK_PISTON_EXTEND,
          1.0f,
          1.0f
        );
      }
    }
  }

  public void teleportPlatform(Location destination) {
    platform.move(destination, true, true);
  }
}

Example: Elevator System

public class Elevator {

  private MovableCuboid cabin;
  private int currentFloor = 0;
  private final int floorHeight = 5;

  public void createElevator(Location base) {
    Location corner1 = base.clone();
    Location corner2 = base.clone().add(5, 3, 5);

    cabin = new MovableCuboid(corner1, corner2);

    // Build cabin walls
    cabin.setMaterial(Material.QUARTZ_BLOCK);

    // Clear inside
    Cuboid interior = new Cuboid(
      corner1.clone().add(1, 0, 1),
      corner2.clone().subtract(1, 0, 1)
    );
    interior.setMaterial(Material.AIR);
  }

  public void goToFloor(int floor) {
    if (floor == currentFloor) return;

    int yDiff = (floor - currentFloor) * floorHeight;
    Vector offset = new Vector(0, yDiff, 0);

    // Move cabin with passengers
    cabin.add(offset, true, true);
    currentFloor = floor;

    // Notify passengers
    for (Entity entity : cabin.getEntities()) {
      if (entity instanceof Player player) {
        player.sendMessage(
          Component.text("Floor " + floor, NamedTextColor.GOLD)
        );
      }
    }
  }

  public void goUp() {
    goToFloor(currentFloor + 1);
  }

  public void goDown() {
    if (currentFloor > 0) {
      goToFloor(currentFloor - 1);
    }
  }
}

MemoryCuboid

MemoryCuboid extends Cuboid with state saving and restoration.

Creation

Location corner1 = new Location(world, 0, 64, 0);
Location corner2 = new Location(world, 10, 74, 10);

// Automatically saves initial state
MemoryCuboid memory = new MemoryCuboid(corner1, corner2);

Save Operations

// Save only blocks (BlockData of each block)
memory.saveBlocks();

// Save only entities (type and location)
memory.saveEntities();

// Save both blocks and entities
memory.saveAll();

Restore Operations

// Restore blocks to saved state
memory.restoreBlocks();

// Restore entities to saved state
memory.restoreEntities();

// Restore both blocks and entities
memory.restoreAll();

Remove Entities

// Remove all entities inside the cuboid
memory.removeAllEntities();

Animated Restoration

// Restore layer by layer from bottom to top (20 ticks per layer)
memory.removeBlocksEverySecond(20L, false);

// Restore from top to bottom (10 ticks per layer)
memory.removeBlocksEverySecond(10L, true);

Example: Arena Manager

public class ArenaManager {

  private MemoryCuboid arena;

  public void setupArena(Location corner1, Location corner2) {
    // Create and save initial state
    arena = new MemoryCuboid(corner1, corner2);

    getLogger().info("Arena saved!");
  }

  public void startGame() {
    // Clear entities before starting
    arena.removeAllEntities();

    getLogger().info("Game started!");
  }

  public void resetArena() {
    // Remove all mobs, items, projectiles
    arena.removeAllEntities();

    // Restore blocks to original state
    arena.restoreBlocks();

    Bukkit.broadcast(
      Component.text("Arena has been reset!", NamedTextColor.GREEN)
    );
  }

  public void animatedReset() {
    arena.removeAllEntities();

    // Restore with animation (1 second per layer)
    arena.removeBlocksEverySecond(20L, true);
  }

  public void updateSaveState() {
    // Save current state as new restore point
    arena.saveAll();

    getLogger().info("Arena state updated!");
  }
}

Example: Building Backup

public class BuildingBackup {

  private final Map<String, MemoryCuboid> backups = new HashMap<>();

  public void createBackup(String name, Location corner1, Location corner2) {
    MemoryCuboid backup = new MemoryCuboid(corner1, corner2);
    backups.put(name, backup);

    Bukkit.broadcast(
      Component.text("Backup '" + name + "' created!", NamedTextColor.GREEN)
    );
  }

  public void restoreBackup(String name) {
    MemoryCuboid backup = backups.get(name);

    if (backup == null) {
      getLogger().warning("Backup not found: " + name);
      return;
    }

    backup.restoreAll();

    Bukkit.broadcast(
      Component.text("Backup '" + name + "' restored!", NamedTextColor.AQUA)
    );
  }

  public void updateBackup(String name) {
    MemoryCuboid backup = backups.get(name);

    if (backup != null) {
      backup.saveAll();
      getLogger().info("Backup updated: " + name);
    }
  }
}

Example: Explosion Protection

public class ExplosionProtection implements Listener {

  private final Map<String, MemoryCuboid> protectedZones = new HashMap<>();

  public void createZone(String name, Location corner1, Location corner2) {
    MemoryCuboid zone = new MemoryCuboid(corner1, corner2);
    protectedZones.put(name, zone);
  }

  @EventHandler
  public void onExplosion(EntityExplodeEvent event) {
    Location explosion = event.getLocation();

    for (Map.Entry<String, MemoryCuboid> entry : protectedZones.entrySet()) {
      MemoryCuboid zone = entry.getValue();

      if (zone.isLocationIn(explosion)) {
        // Cancel explosion
        event.setCancelled(true);

        // Restore any damaged blocks
        zone.restoreBlocks();

        // Play sound
        explosion.getWorld().playSound(
          explosion,
          Sound.BLOCK_ANVIL_LAND,
          1.0f,
          1.0f
        );

        getLogger().info("Protected zone: " + entry.getKey());
        return;
      }
    }
  }

  @EventHandler
  public void onBlockBreak(BlockBreakEvent event) {
    Block block = event.getBlock();

    for (MemoryCuboid zone : protectedZones.values()) {
      if (zone.isLocationIn(block.getLocation())) {
        Player player = event.getPlayer();

        if (!player.hasPermission("zone.edit")) {
          event.setCancelled(true);
          player.sendMessage(
            Component.text("This zone is protected!", NamedTextColor.RED)
          );
        }
      }
    }
  }
}

Complete Examples

Building Generator

public class BuildingGenerator {

  public void generateHouse(Location start) {
    World world = start.getWorld();

    // Floor
    Cuboid floor = new Cuboid(
      start.clone(),
      start.clone().add(10, 0, 10)
    );
    floor.setMaterial(Material.OAK_PLANKS);

    // Walls
    Cuboid walls = new Cuboid(
      start.clone().add(0, 1, 0),
      start.clone().add(10, 5, 10)
    );
    walls.setMaterial(Material.OAK_PLANKS);

    // Hollow inside
    Cuboid hollow = new Cuboid(
      start.clone().add(1, 1, 1),
      start.clone().add(9, 4, 9)
    );
    hollow.setMaterial(Material.AIR);

    // Roof
    Cuboid roof = new Cuboid(
      start.clone().add(0, 6, 0),
      start.clone().add(10, 6, 10)
    );
    roof.setMaterial(Material.DARK_OAK_PLANKS);
  }

  public void animatedBuild(Location start) {
    Cuboid building = new Cuboid(
      start.clone(),
      start.clone().add(10, 10, 10)
    );

    // Build from bottom to top (3 ticks per layer)
    building.setMaterialEveryTick(Material.STONE_BRICKS, 3L, false);
  }
}

Ore Scanner

public class OreScanner {

  public void scanArea(Player player, int radius) {
    Location center = player.getLocation();

    Cuboid scanArea = new Cuboid(
      center.clone().subtract(radius, radius, radius),
      center.clone().add(radius, radius, radius)
    );

    // Count ores
    int diamond = scanArea.countBlocksOfMaterial(Material.DIAMOND_ORE);
    int gold = scanArea.countBlocksOfMaterial(Material.GOLD_ORE);
    int iron = scanArea.countBlocksOfMaterial(Material.IRON_ORE);
    int coal = scanArea.countBlocksOfMaterial(Material.COAL_ORE);

    // Send results
    player.sendMessage(
      Component.text("=== Ore Scan ===", NamedTextColor.GOLD)
    );
    player.sendMessage(
      Component.text("Diamond: " + diamond, NamedTextColor.AQUA)
    );
    player.sendMessage(
      Component.text("Gold: " + gold, NamedTextColor.YELLOW)
    );
    player.sendMessage(
      Component.text("Iron: " + iron, NamedTextColor.GRAY)
    );
    player.sendMessage(
      Component.text("Coal: " + coal, NamedTextColor.DARK_GRAY)
    );

    // Show scan area
    scanArea.show(player, radius, true, new Vector(1, 1, 1));
  }
}

Teleportation Pad

public class TeleportPad implements Listener {

  private final Map<Cuboid, Location> pads = new HashMap<>();

  public void createPad(Location corner1, Location corner2, Location destination) {
    Cuboid pad = new Cuboid(corner1, corner2);

    // Build pad
    pad.setMaterial(Material.AMETHYST_BLOCK);

    // Register
    pads.put(pad, destination);
  }

  @EventHandler
  public void onPlayerMove(PlayerMoveEvent event) {
    Player player = event.getPlayer();
    Location to = event.getTo();

    if (to == null) return;

    for (Map.Entry<Cuboid, Location> entry : pads.entrySet()) {
      if (entry.getKey().isLocationIn(to)) {
        player.teleport(entry.getValue());
        player.playSound(
          player.getLocation(),
          Sound.ENTITY_ENDERMAN_TELEPORT,
          1.0f,
          1.0f
        );
        return;
      }
    }
  }
}

Best Practices

✅ Do's

  • Validate locations — Check for null and same world
  • Use appropriate type — Basic for static, Movable for dynamic, Memory for state
  • Async large operations — Use Bukkit scheduler for big cuboids
  • Use animations — Layer-by-layer for better visuals
  • Clean up — Remove unused cuboids
  • Boundary extensions — For precise entity detection
  • Check performance — Limit cuboid size

❌ Don'ts

  • Don't create huge cuboids — Can cause lag
  • Don't mix worlds — Always check world equality
  • Don't forget validation — Null checks are important
  • Don't over-animate — Too many animations = lag
  • Don't ignore async — Use async for large operations
  • Don't leak memory — Clean up properly

API Reference

Cuboid Methods

Method Description
getCenter() Get 3D center location
getCenterXZ() Get XZ center (Y at min + 1)
getBlocks() Get all blocks
getEntities() Get entities with default boundary
getEntities(extension) Get entities with boundary extension
isLocationIn(location) Check if location is inside
isLocationIn(location, boundary) Check with custom boundary
setMaterial(material) Fill with material
setBlockData(data) Fill with BlockData
replaceMaterial(old, new) Replace material
replaceBlockData(old, new) Replace BlockData
countBlocksOfMaterial(material) Count material
countBlocksOfBlockData(data) Count BlockData
setMaterialEveryTick(material, tick, topToBottom) Animate fill
setBlockDataEveryTick(data, tick, topToBottom) Animate fill with BlockData
replaceMaterialEveryTick(old, new, tick, topToBottom) Animate replace
replaceBlockDataEveryTick(old, new, tick, topToBottom) Animate replace BlockData
show(player, radius, bordersOnly, extension) Show with particles

MovableCuboid Methods

Method Description
move(center, moveEntities, moveBlocks) Move to new center
add(vector, moveEntities, moveBlocks) Shift by vector

MemoryCuboid Methods

Method Description
saveBlocks() Save block state
saveEntities() Save entity state
saveAll() Save blocks and entities
restoreBlocks() Restore blocks
restoreEntities() Restore entities
restoreAll() Restore all
removeAllEntities() Remove entities
removeBlocksEverySecond(tick, topToBottom) Animate restoration

Next Steps

  • 📦 CuboidService — Global cuboid management
  • Scheduler — Schedule cuboid operations
  • 🎯 Event — Listen to block/entity events

Clone this wiki locally