Skip to content

Carving terrain ​

CarveApi cuts land far faster than setting blocks one at a time, and it spreads the work over several ticks. Call it on the server thread.

Make a cut ​

Build a cut, then call start(). Rimulib ticks it until it finishes.

java
// A round hole down to the bottom of the world
CarveApi.column(level, centre, 40).start();

// Level a disc of land down to y 64
CarveApi.flatten(level, centre, 60, 64).named("Landing pad").start();

Shapes ​

MethodShape
columnA round hole down to the bottom of the world
flattenA disc levelled to one height, or to any height you choose per column
slitA north to south slit whose width can change along its length
wedgeA fan that opens along a heading
lineA straight trench
lensA lens-shaped cut along a heading

Choose the floor yourself ​

A Floor returns the height for each column. Everything above that height is removed. Return CarveApi.UNTOUCHED to skip a column.

A Surface decides what the block left at the floor becomes. Return null to keep it. You can also pass null as the whole surface.

java
CarveApi.Floor bowl = (x, z) -> {
    double d = Math.hypot(x - centre.x, z - centre.z);
    return d > 50 ? CarveApi.UNTOUCHED : 60 + (int) (d * d / 100);
};

CarveApi.Surface scorch = (x, y, z, was) -> Blocks.BLACKSTONE.defaultBlockState();

CarveApi.flatten(level, centre, 50, bowl, scorch).start();

Floors and surfaces run on the server thread, so keep them fast.

Control a running cut ​

start() returns the same Cut, so you can keep it:

MethodWhat it does
done()Whether the cut has finished
stop()Ends the cut early
finish()Finishes now and blocks the server thread until done
chunks()The chunks in the cut, nearest first

To drive a cut yourself, skip start() and call tick() once per server tick. tick(front) only cuts as far as front blocks out, which is useful when the cut should follow something moving.

Large cuts ​

  • perTick(n) sets how many chunks are handled at a time.
  • lazy() only cuts loaded chunks and comes back for the rest as they load.
  • streaming(hold, release) also loads chunks for the cut. Pass two functions that add and remove your own chunk tickets.

Protect your blocks ​

Register the blocks once, then opt in on each cut with keepBlocks():

java
CarveApi.keepBlocks(state -> state.is(MyBlocks.CORE));

CarveApi.column(level, centre, 40).keepBlocks().start();

The spared overloads of column and flatten leave a 5 by 5 group of columns standing around one position.

Write many blocks at once ​

paint writes straight into chunk sections, then relights and resends them. Containers replaced this way lose their contents.

java
Map<BlockPos, BlockState> blocks = new HashMap<>();
blocks.put(pos, Blocks.STONE.defaultBlockState());
CarveApi.paint(level, blocks);

Columns under an active ward are left as they are. See Erasing and wards.

Hooks ​

MethodWhen it runs
watchEvery block a cut or write replaces
joinWithFixes a painted block from its neighbours, such as connected textures
onTimeTurnWhen turnTime(level) is called, so you can undo your own work

turnTime(level) tells your listeners, then drops every cut still running in that level.