-
-
Notifications
You must be signed in to change notification settings - Fork 0
Cuboid
Samuel De Oliveira edited this page Jan 12, 2026
·
1 revision
Manipulate 3D rectangular regions with advanced block and entity operations.
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.
| 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 |
// 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();Location corner1 = new Location(world, 0, 64, 0);
Location corner2 = new Location(world, 10, 74, 10);
Cuboid cuboid = new Cuboid(corner1, corner2);// Get 3D center (middle of X, Y, Z)
Location center = cuboid.getCenter();
player.teleport(center);// Get center on XZ plane (Y at lowest point + 1)
Location centerXZ = cuboid.getCenterXZ();
// Useful for ground-level teleports
player.teleport(centerXZ);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)
);
}
}// 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);// 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 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);BlockData oldData = Bukkit.createBlockData(Material.STONE);
BlockData newData = Bukkit.createBlockData(Material.GLOWSTONE);
cuboid.replaceBlockData(oldData, newData);// 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);// 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);BlockData data = Bukkit.createBlockData(Material.BLUE_STAINED_GLASS);
// Bottom to top
cuboid.setBlockDataEveryTick(data, 5L, false);
// Top to bottom
cuboid.setBlockDataEveryTick(data, 5L, true);// 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
);BlockData oldData = Bukkit.createBlockData(Material.STONE);
BlockData newData = Bukkit.createBlockData(Material.DIAMOND_BLOCK);
cuboid.replaceBlockDataEveryTick(oldData, newData, 5L, false);// 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)
);
}
}// 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);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)
);if (cuboid.isLocationIn(player.getLocation())) {
player.sendMessage(
Component.text("You are inside!", NamedTextColor.GREEN)
);
} else {
player.sendMessage(
Component.text("You are outside!", NamedTextColor.RED)
);
}Vector boundary = new Vector(5, 0, 5);
if (cuboid.isLocationIn(location, boundary)) {
getLogger().info("Location is inside with extended boundary");
}// Show all blocks within 30 block radius
cuboid.show(player, 30.0, false, new Vector(1, 1, 1));// Show only the edges (more performant)
cuboid.show(player, 50.0, true, new Vector(1, 1, 1));// Show with 2 block extension on X and Z
Vector extension = new Vector(2, 0, 2);
cuboid.show(player, 40.0, true, extension);MovableCuboid extends Cuboid with movement capabilities.
Location corner1 = new Location(world, 0, 64, 0);
Location corner2 = new Location(world, 10, 74, 10);
MovableCuboid movable = new MovableCuboid(corner1, corner2);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);// 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);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);
}
}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 extends Cuboid with state saving and restoration.
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 only blocks (BlockData of each block)
memory.saveBlocks();
// Save only entities (type and location)
memory.saveEntities();
// Save both blocks and entities
memory.saveAll();// Restore blocks to saved state
memory.restoreBlocks();
// Restore entities to saved state
memory.restoreEntities();
// Restore both blocks and entities
memory.restoreAll();// Remove all entities inside the cuboid
memory.removeAllEntities();// 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);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!");
}
}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);
}
}
}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)
);
}
}
}
}
}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);
}
}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));
}
}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;
}
}
}
}- 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'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
| 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 |
| Method | Description |
|---|---|
move(center, moveEntities, moveBlocks) |
Move to new center |
add(vector, moveEntities, moveBlocks) |
Shift by vector |
| 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 |
- 📦 CuboidService — Global cuboid management
- ⚡ Scheduler — Schedule cuboid operations
- 🎯 Event — Listen to block/entity events