Skip to content

Instantly share code, notes, and snippets.

@zenril
Created March 2, 2025 07:06
Show Gist options
  • Save zenril/be9abee22b6b072a4777a404637353de to your computer and use it in GitHub Desktop.
Save zenril/be9abee22b6b072a4777a404637353de to your computer and use it in GitHub Desktop.

Below is a comprehensive Markdown document that explains what the script does, how it works, and the commands/parameters you can use.


Gravity CraftScript Documentation

This CraftScript allows you to move a specified block type one block at a time within your WorldEdit selection. The movement can be performed in various directions—vertical (up, down), cardinal (north, east, south, west), and relative to your position (left, right, forward, back). The script processes the entire region in memory (by building a 3D array of the blocks), performs the movement for a specified number of iterations (or a percentage of the region’s maximum moves), and then updates the world in one pass.


Table of Contents


Overview

This script—invoked with /cs gravity—is designed to “move” blocks in your selected region by swapping their positions with their neighbors. Each movement (or iteration) shifts the target block one cell in the chosen direction. The script supports:

  • Vertical Movement: up (u) and down (d)
  • Cardinal Directions: north (n), east (e), south (s), and west (w)
  • Relative Movement: left (l), right (r), forward (f), and back (b)

Additionally, you can specify the number of iterations either as a plain number or as a percentage (e.g., "50%") which is calculated against the maximum possible moves in that axis.


Usage

/cs gravity <direction> <block> [iterations]
  • <direction>: Specifies the direction to move the target block.
    Allowed values include both full names and short forms:
    • Vertical: up (u), down (d)
    • Relative: left (l), right (r), forward (f), back (b)
    • Cardinal: north (n), east (e), south (s), west (w)
  • <block>: The block type you want to move (e.g., mossy_block, stone, etc.).
  • [iterations]: (Optional) A positive integer or a percentage (e.g., "50%") that defines how many times the move is applied.
    • When given as a percentage, the script calculates the maximum number of moves based on the region's size along the relevant axis and then uses that percentage to determine the iterations.

Parameters

Direction

The script accepts both full and abbreviated forms:

  • Vertical:

    • up (or u) – Moves the block one cell upward.
    • down (or d) – Moves the block one cell downward.
  • Relative (based on player's facing):

    • left (or l) – Moves the block one cell to the left relative to the player.
    • right (or r) – Moves the block one cell to the right relative to the player.
    • forward (or f) – Moves the block one cell in the direction the player is facing.
    • back (or b) – Moves the block one cell opposite the direction the player is facing.
  • Cardinal (absolute):

    • north (or n) – Moves the block one cell north (decreasing Z).
    • east (or e) – Moves the block one cell east (increasing X).
    • south (or s) – Moves the block one cell south (increasing Z).
    • west (or w) – Moves the block one cell west (decreasing X).

Block Type

Specify the target block that will be moved. The script uses the WorldEdit context.getBlock() function to parse this argument and validate it against known block types.

Iterations

  • Numeric Value:
    For example, 3 will move the target block 3 cells in the chosen direction (one cell per iteration).

  • Percentage Value:
    For example, 50% calculates the maximum possible moves along the relevant axis (for instance, for vertical movement, if the region’s height is 10 blocks, the maximum moves is 9) and uses that percentage to determine the number of iterations (e.g., 50% of 9 → 4 iterations).


How the Script Works

  1. Input Validation and Parsing:

    • The script verifies that a valid selection exists.
    • It then maps any short direction codes (e.g., u, f) to their full names.
    • The block type is parsed and validated.
    • The iterations parameter is parsed as either an integer or a percentage, calculating the number of iterations accordingly.
  2. Building the In-Memory Representation:

    • The script computes the region's dimensions and loads every block in the region into a 3D array.
    • This minimizes repeated API calls and lets the script simulate the block movement in memory.
  3. Simulating Block Movement:

    • For each iteration, the script scans through the 3D array and, when it finds a block that matches the target block, swaps it with the neighboring cell in the desired direction.
    • The swapping order is carefully chosen based on the movement direction to ensure that each block only moves one step per iteration.
  4. Updating the World:

    • After processing all iterations in memory, the script writes the updated block states back to the world in a single pass, applying one setBlock call per coordinate.

Examples

  • Move mossy blocks upward 3 times:

    /cs gravity up mossy_block 3
    
  • Move stone blocks forward by 50% of the maximum moves possible in that direction:

    /cs gravity forward stone 50%
    
  • Move target blocks to the left (relative to player) 5 times:

    /cs gravity l target_block 5
    
  • Move blocks south by 10 iterations:

    /cs gravity south block_name 10
    

Notes and Requirements

  • WorldEdit Setup:
    Ensure that your script is placed in the correct CraftScripts folder in your WorldEdit configuration (e.g., plugins/WorldEdit/craftscripts for server setups).

  • Block Comparison:
    The script compares blocks using their string representations (toString()). Make sure that the block names you provide match the names expected by WorldEdit.

  • In-Memory Processing:
    Because the script processes all blocks in memory, it is optimized for fewer API calls. However, for very large selections, be aware of possible performance impacts.

  • Relative Directions:
    The directions forward, back, left, and right are calculated based on the player’s current yaw (facing direction). Ensure you are facing the intended direction before executing the command.


This comprehensive script gives you great flexibility to “move” blocks within a region in any direction using both absolute (cardinal) and relative (player-based) coordinates, with dynamic iteration control. Enjoy editing your world with Gravity CraftScript!

// Description: Moves a specified block type one block in the specified direction
// ("up", "down", "left", "right", "forward", "back", "north", "east", "south", or "west")
// within your selected region, performing the operation in-memory and then applying all changes in one pass.
// Usage: /cs gravity <direction> <block> [iterations]
// Valid directions (full or short):
// up (u), down (d), left (l), right (r),
// forward (f), back (b),
// north (n), east (e), south (s), west (w)
// Iterations can be a positive number or a percentage (e.g., "50%")
importPackage(Packages.com.sk89q.worldedit);
importPackage(Packages.com.sk89q.worldedit.world);
importPackage(Packages.com.sk89q.worldedit.block);
importPackage(Packages.com.sk89q.worldedit.regions);
importPackage(Packages.com.sk89q.worldedit.math);
importPackage(Packages.com.sk89q.worldedit.blocks);
importPackage(java.lang);
(function init(context, argv) {
var editSession = context.remember();
var session = context.getSession();
var player = context.getPlayer();
var region = session.getRegionSelector(player.getWorld()).getRegion();
context.checkArgs(2, 3, "<direction> <block> [iterations]");
let args = argv.slice(1);
if (region == null) {
context.print("No selection found!");
return;
}
// Calculate region dimensions.
var min = region.getMinimumPoint();
var max = region.getMaximumPoint();
var dx = max.getX() - min.getX() + 1;
var dy = max.getY() - min.getY() + 1;
var dz = max.getZ() - min.getZ() + 1;
// Get the mode and map any short forms to full names.
var mode = args[0].toLowerCase();
var modeMapping = {
"u": "up",
"d": "down",
"l": "left",
"r": "right",
"f": "forward",
"b": "back",
"n": "north",
"e": "east",
"s": "south",
"w": "west"
};
if (modeMapping[mode] !== undefined) {
mode = modeMapping[mode];
}
var blockName = args[1].toLowerCase();
var targetBlock = context.getBlock(blockName);
if (targetBlock == null) {
context.print("Block type '" + blockName + "' not found!");
return;
}
// Determine the number of iterations.
var iterations = 1;
if (args.length >= 3) {
var iterArg = args[2];
if (iterArg.indexOf("%") !== -1) {
var percent = parseFloat(iterArg.replace("%", ""));
if (isNaN(percent) || percent < 0) {
context.print("Invalid percentage for iterations.");
return;
}
var maxIterations;
if (mode == "up" || mode == "down") {
maxIterations = dy - 1;
} else if (mode == "north" || mode == "south") {
maxIterations = dz - 1;
} else if (mode == "east" || mode == "west") {
maxIterations = dx - 1;
} else if (mode == "left" || mode == "right" || mode == "forward" || mode == "back") {
// For relative horizontal movement, decide axis based on player's facing.
var yaw = player.getLocation().getYaw();
var angle = ((yaw % 360) + 360) % 360;
// For left/right and forward/back, we choose the dominant axis.
if (angle >= 315 || angle < 45 || (angle >= 135 && angle < 225)) {
maxIterations = dx - 1;
} else {
maxIterations = dz - 1;
}
}
iterations = Math.floor(maxIterations * (percent / 100));
if (iterations < 1) iterations = 1;
} else {
iterations = parseInt(iterArg);
if (isNaN(iterations) || iterations < 1) {
context.print("Invalid iterations parameter. It must be a positive integer or a percentage.");
return;
}
}
}
// Build a 3D array of blocks representing the region.
var blocks = new Array(dx);
for (var x = 0; x < dx; x++) {
blocks[x] = new Array(dy);
for (var y = 0; y < dy; y++) {
blocks[x][y] = new Array(dz);
for (var z = 0; z < dz; z++) {
var pos = BlockVector3.at(min.getX() + x, min.getY() + y, min.getZ() + z);
blocks[x][y][z] = editSession.getBlock(pos);
}
}
}
// Perform the specified number of iterations in memory.
for (var iter = 0; iter < iterations; iter++) {
if (mode == "up") {
for (var x = 0; x < dx; x++) {
for (var z = 0; z < dz; z++) {
for (var y = dy - 2; y >= 0; y--) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var temp = blocks[x][y+1][z];
blocks[x][y+1][z] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
} else if (mode == "down") {
for (var x = 0; x < dx; x++) {
for (var z = 0; z < dz; z++) {
for (var y = 1; y < dy; y++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var temp = blocks[x][y][z];
var temp2 = blocks[x][y-1][z];
blocks[x][y-1][z] = temp;
blocks[x][y][z] = temp2;
}
}
}
}
} else if (mode == "left" || mode == "right") {
// Relative left/right based on player's facing.
var yaw = player.getLocation().getYaw();
var angle = ((yaw % 360) + 360) % 360;
var offsetX = 0, offsetZ = 0;
if (angle >= 315 || angle < 45) {
if (mode == "left") { offsetX = 1; }
else { offsetX = -1; }
} else if (angle >= 45 && angle < 135) {
if (mode == "left") { offsetZ = 1; }
else { offsetZ = -1; }
} else if (angle >= 135 && angle < 225) {
if (mode == "left") { offsetX = -1; }
else { offsetX = 1; }
} else { // angle >= 225 && angle < 315
if (mode == "left") { offsetZ = -1; }
else { offsetZ = 1; }
}
if (offsetX != 0) {
if (offsetX > 0) {
for (var x = dx - 2; x >= 0; x--) {
for (var y = 0; y < dy; y++) {
for (var z = 0; z < dz; z++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newX = x + offsetX;
if (newX >= 0 && newX < dx) {
var temp = blocks[newX][y][z];
blocks[newX][y][z] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else {
for (var x = 1; x < dx; x++) {
for (var y = 0; y < dy; y++) {
for (var z = 0; z < dz; z++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newX = x + offsetX;
if (newX >= 0 && newX < dx) {
var temp = blocks[newX][y][z];
blocks[newX][y][z] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
}
}
if (offsetZ != 0) {
if (offsetZ > 0) {
for (var z = dz - 2; z >= 0; z--) {
for (var x = 0; x < dx; x++) {
for (var y = 0; y < dy; y++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newZ = z + offsetZ;
if (newZ >= 0 && newZ < dz) {
var temp = blocks[x][y][newZ];
blocks[x][y][newZ] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else {
for (var z = 1; z < dz; z++) {
for (var x = 0; x < dx; x++) {
for (var y = 0; y < dy; y++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newZ = z + offsetZ;
if (newZ >= 0 && newZ < dz) {
var temp = blocks[x][y][newZ];
blocks[x][y][newZ] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
}
}
} else if (mode == "forward") {
// Relative forward based on player's facing.
var yaw = player.getLocation().getYaw();
var angle = ((yaw % 360) + 360) % 360;
var offsetX = 0, offsetZ = 0;
if (angle >= 315 || angle < 45) {
offsetX = 0; offsetZ = 1;
} else if (angle >= 45 && angle < 135) {
offsetX = -1; offsetZ = 0;
} else if (angle >= 135 && angle < 225) {
offsetX = 0; offsetZ = -1;
} else {
offsetX = 1; offsetZ = 0;
}
if (offsetX != 0) {
if (offsetX > 0) {
for (var x = dx - 2; x >= 0; x--) {
for (var y = 0; y < dy; y++) {
for (var z = 0; z < dz; z++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newX = x + offsetX;
if (newX >= 0 && newX < dx) {
var temp = blocks[newX][y][z];
blocks[newX][y][z] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else {
for (var x = 1; x < dx; x++) {
for (var y = 0; y < dy; y++) {
for (var z = 0; z < dz; z++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newX = x + offsetX;
if (newX >= 0 && newX < dx) {
var temp = blocks[newX][y][z];
blocks[newX][y][z] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
}
}
if (offsetZ != 0) {
if (offsetZ > 0) {
for (var z = dz - 2; z >= 0; z--) {
for (var x = 0; x < dx; x++) {
for (var y = 0; y < dy; y++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newZ = z + offsetZ;
if (newZ >= 0 && newZ < dz) {
var temp = blocks[x][y][newZ];
blocks[x][y][newZ] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else {
for (var z = 1; z < dz; z++) {
for (var x = 0; x < dx; x++) {
for (var y = 0; y < dy; y++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newZ = z + offsetZ;
if (newZ >= 0 && newZ < dz) {
var temp = blocks[x][y][newZ];
blocks[x][y][newZ] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
}
}
} else if (mode == "back") {
// Relative back is the opposite of forward.
var yaw = player.getLocation().getYaw();
var angle = ((yaw % 360) + 360) % 360;
var offsetX = 0, offsetZ = 0;
if (angle >= 315 || angle < 45) {
offsetX = 0; offsetZ = -1;
} else if (angle >= 45 && angle < 135) {
offsetX = 1; offsetZ = 0;
} else if (angle >= 135 && angle < 225) {
offsetX = 0; offsetZ = 1;
} else {
offsetX = -1; offsetZ = 0;
}
if (offsetX != 0) {
if (offsetX > 0) {
for (var x = dx - 2; x >= 0; x--) {
for (var y = 0; y < dy; y++) {
for (var z = 0; z < dz; z++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newX = x + offsetX;
if (newX >= 0 && newX < dx) {
var temp = blocks[newX][y][z];
blocks[newX][y][z] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else {
for (var x = 1; x < dx; x++) {
for (var y = 0; y < dy; y++) {
for (var z = 0; z < dz; z++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newX = x + offsetX;
if (newX >= 0 && newX < dx) {
var temp = blocks[newX][y][z];
blocks[newX][y][z] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
}
}
if (offsetZ != 0) {
if (offsetZ > 0) {
for (var z = dz - 2; z >= 0; z--) {
for (var x = 0; x < dx; x++) {
for (var y = 0; y < dy; y++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newZ = z + offsetZ;
if (newZ >= 0 && newZ < dz) {
var temp = blocks[x][y][newZ];
blocks[x][y][newZ] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else {
for (var z = 1; z < dz; z++) {
for (var x = 0; x < dx; x++) {
for (var y = 0; y < dy; y++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newZ = z + offsetZ;
if (newZ >= 0 && newZ < dz) {
var temp = blocks[x][y][newZ];
blocks[x][y][newZ] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
}
}
} else if (mode == "east") {
for (var y = 0; y < dy; y++) {
for (var z = 0; z < dz; z++) {
for (var x = dx - 2; x >= 0; x--) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newX = x + 1;
if (newX < dx) {
var temp = blocks[newX][y][z];
blocks[newX][y][z] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else if (mode == "west") {
for (var y = 0; y < dy; y++) {
for (var z = 0; z < dz; z++) {
for (var x = 1; x < dx; x++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newX = x - 1;
if (newX >= 0) {
var temp = blocks[newX][y][z];
blocks[newX][y][z] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else if (mode == "south") {
for (var x = 0; x < dx; x++) {
for (var y = 0; y < dy; y++) {
for (var z = dz - 2; z >= 0; z--) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newZ = z + 1;
if (newZ < dz) {
var temp = blocks[x][y][newZ];
blocks[x][y][newZ] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else if (mode == "north") {
for (var x = 0; x < dx; x++) {
for (var y = 0; y < dy; y++) {
for (var z = 1; z < dz; z++) {
if (blocks[x][y][z].toString() == targetBlock.toString()) {
var newZ = z - 1;
if (newZ >= 0) {
var temp = blocks[x][y][newZ];
blocks[x][y][newZ] = blocks[x][y][z];
blocks[x][y][z] = temp;
}
}
}
}
}
} else {
context.print("Invalid mode. Use 'up' (u), 'down' (d), 'left' (l), 'right' (r), 'forward' (f), 'back' (b), 'north' (n), 'east' (e), 'south' (s), or 'west' (w).");
return;
}
}
// Write all changes back to the world in one pass.
for (var x = 0; x < dx; x++) {
for (var y = 0; y < dy; y++) {
for (var z = 0; z < dz; z++) {
var pos = BlockVector3.at(min.getX() + x, min.getY() + y, min.getZ() + z);
editSession.setBlock(pos, blocks[x][y][z]);
}
}
}
})(context, argv);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment