Abstract
This proposal describes a world edit plugin largely based on features originally intended in WorldEditArt, along with some new ideas.
Users of this plugin should be managed in ”builder sessions” with individual access information. Builder sessions can be created in three modes, namely:
Implicit Mode Players with a certain permission will start a builder session upon joining a game. The session’s permission and location will be synchronized with the player.
Explicit Mode Players with a certain permission will start a builder session upon typing a command. The session’s permission and location will be synchronized with the player, and can be closed with a command. The command may be locked with a private password (similar to the sudo Linux command) or global password (similar to the su Linux command) for additional protection.
Minion Mode Command senders with a certain permission (especially non-in-game senders like console) can create minion builder sessions upon typing a command. The session’s permission and location will be controlled by the command sender. Each builder session has an allocated amount of resources; this allocation may affect the rate of world-editing operations to maximize server performance.
For implicit and explicit modes, the builder session’s position and orientation is synchronized with the player. The position uses the block that the player’s feet stand in.
A selection refers to a 3D shape. A selection can be created by various methods as described in the Shape Selection section.
By default, all commands only affect the selection named ”default”. Builder sessions may also create other selections, identified by case-insensitive names. The selections should be discarded upon closing a builder session (e.g. when a player quits).
A selection should provide an iterator providing a unique stream of blocks within the selection, or blocks on the margin of the selection at a defined padding (inside the border) and margin (outside the border).
A block changer is an interface that accepts a Block argument and returns another Block argument, determining the new block to set.
Four implementations of block changer are proposed:
Simple block changer The blocks are always set to the specified block type.
Repeating block changer A list of block types is provided by the user, and the blocks are returned in a loop. For example, if the user specifies 1 glowstone, 2 glass, 3 stone, 1 lantern, the first block set is a glowstone, the next two are glass, etc. This loop repeats at the 9th block. This is useful for generating patterns in a rectangle or a cuboid, but the direction is undefined.
Random list block changer A list of block types is provided by the user. Each block is set to one of the block types in the list randomly selected.
Weighted random list block changer Same as random list block changer, except that the block types have different probability of being selected.
An alternative chain is Clipboard -> Cassette -> User History -> Load Synchronizer, where the clipboard provides both the block iterator and the block changer. More details will be described in the Clipboard section.
A cassette is a section of server memory, or a temporary file on the hard disk (if greater than 10MB), storing an ordered list of blocks changed in an operation. It can be re-executed in forward or backward order to redo/undo the operation.
A user history manages an ”undo stack” and a ”redo stack”, each holding an ordered list of cassettes. When the user requests to undo or redo, a cassette from one stack will be moved to the other stack, and pass a reference to the cassette to the load synchronizer.
The load synchronizer manages chunk locking and cassette operation queuing. Before starting an operation, it determines whether the operation should be executed synchronously or asynchronously and stores the chunks (identifiers only) affected by the cassette to manage locking. Cassette operations owned by the same builder session will be executed one by one, but cassette operations owned by different builder sessions are executed orderlessly.
If the selection shape supports reporting the maximum number of blocks changed per chunk, it would determine whether to use the synchronous strategy (updating a few blocks every tick) or the asynchronous strategy (lock the chunks, pass all parameters to an AsyncTask, modify them on the other thread and send the whole chunks).
While the chain consists of five components, the user only needs to make two inputs, and only look at the final user history.
A builder session first creates a selection using specific means (e.g. wands, commands, etc.). After confirming the selection, the user can execute a manipulation command, e.g. //set, //replace, etc. This will trigger the block iterator from the selection and instantiate a block changer from the manipulation command’s arguments. A cassette will be inserted into the user history, and the user history shall pull data from block iterator and block changer into the cassette for execution.
If the load synchronizer contains cassettes used by the user, it should show a progress bar on the user’s screen (scoreboards for players, periodic logger messages (at a lower frequency) for console).
The geometric logic is implemented in libgeom, while the UI logic is implemented in this plugin.
A wand refers to a combination of an item and an action; left-clicking with an emerald and right-clicking with an emerald are considered as two different wands. The clicked block is known as the this position.
A wand can be virtually used using a command. For example, the command //pos1 is equivalent to clicking the pos1 wand at the block that the builder session stands in.
A cuboid is always aligned to the XYZ coordinate system.
Two points named pos1 and pos2. The smallest cuboid inscribing both points is selected.
Explicit selection Execute the wands pos1 and pos2 to select the two positions separately. Executing again manipulates the selection.
shoot command //cuboid shoot <distance> selects the this position as pos1, then shoots a ray along the this orientation for a specified length to obtain pos2.
grow command //cuboid grow <+x> [-x] <+y> [-y] <+z> [-z] modifies the current cuboid selection by extending the cuboid on the specified sides. pos1 and pos2 points might be flipped.
A conical frustum is very flexible. It is the generalization of:
A cylinder is a special case of conical frustum where both base shapes are congruent. A cone is a special case of conical frustum where the top shape has radius 0.
Of which, the base shape can be a circle or an ellipse. The base circle may or may not align to the XYZ coordinate system. The principal axis (the axis of rotation symmetry) may or may not be orthogonal to the XYZ coordinate system, and may or may not be orthogonal to the base shape. However, the top shape (for cylinders and conical frustums) must be parallel to the base shape.
Despite the great flexibility, there are no conditional parameters. The shape is always only parameterized by a base ellipse and a top ellipse.
Base ellipse The base ellipse is defined by a center point baseCenter (a position vector), axis 1 baseAxis1 (a relative vector from the center to one of the extreme points on the ellipse) and axis 2 baseAxis2 (a relative vector orthogonal to baseAxis1 to represent the other elliptic axis).
Top ellipse The top shape must be a stretched projection of the base shape. That is, the top shape has parallel axes 1 and 2 to the base shape, but the lengths can be different. Therefore, the top shape is defined by the center point topCenter, length of axis 1 topLength1 and length of axis 2 topLength2.
Principal axis There are two basic wands baseCenter and topCenter to select the principal axis of the shape.
Upright radius selection If the desired shape is an upright (base shape is orthogonal to the principal axis) circular cylinder, the radius wand selects any point on the extended curved plane of the cylinder. In other words, the distance between the base center and the projection of the selected point on the base plane is used as the radius of the cylinder. If the desired shape is an elliptic cylinder, use radius followed by radius2 to select the intersection points of the curved plane and axes 1/2. If the desired shape is a conical frustum,
Aligned upright radius selection If the elliptic radius is aligned on the XZ/YZ/XY plane, use radiusY/radiusX/radiusZ so that the selected point is projected onto the respective planes.
Orientation changes The freeRadius and freeRadius2 wands freely select the intersection points between the base radii and the base ellipse perimeter.
Top center alignment The topCenterY wand only selects the height of the shape. The selected point is horizontally moved to a point directly above/below baseCenter. If horizontal alignment is desired, use topCenterX or topCenterZ instead.
Top radius selection Add top before the radius selection wand names so that the wand only modifies the top radii but not the base radii, except the base axis directions are rotated if the top radii are also rotated. Orientation-changing wands are not applicable as the two ellipses must be parallel.
Creation A much simpler way to create a basic aligned (elliptic) cylinder is to use the command //cylinder <height> <radius> [radius2] [Y|x|z], where the last parameter is the alignment of the principal axis (default y).
Conversion //cylinder cone sets the top radii to 0, making the shape a cone. //cylinder cylinder sets the top radii to be same as the base radii, making the shape a cylinder. //cylinder frustum <ratio> sets the top radii as <ratio> times of the base radii, making the shape a frustum. cone is equivalent to frustum 0, and cyl is equivalent to frustum 1.
Orthogonalization //cylinder pl projects the base and top ellipses to the planes containing the base and top centers orthogonal to the principal axis. //cylinder rot rotates the base and top ellipses such that they are orthogonal to the principal axis.
Explicit radius //cylinder radius [top|base] [major|minor] <length> sets the major/minor/both radii of the top/base/both ellipses to <length>.
An ellipsoid is a sphere that can be stretched alogn the three axes respectively.
center is the center point of the ellipsoid, and rx, ry and rz are the radii of the ellipsoid on the three axes.
Use the center wand to select the center of the sphere. Use rx, ry and rz to select any point at the same x/y/z-coordinate as the margins of the ellipsoid. If other radii have not been set, the wands also set them to the same value. If the ellipsoid is a regular sphere, it is also possible to use radius to select any point on the sphere surface.
Mould selection is a non-geometric selection. There are multiple mould selection modes:
Walking The blocks that have intersected with the player during the selection are added to the set of selected positions.
Interaction The blocks destroyed/placed by the player during the selection are added to the set of selected positions.
Filtering The blocks inside an existing selection that match a certain criterion are added to the new selection. Criteria include:
Block whitelist/blacklist Only certain block types are included/excluded.
Set intersection/subtraction Only blocks inside/not inside another selection are included.
//copy iterates the current selection and writes each block (coordinates + block type) to a schematic file. Each builder session may own their own directory of multiple clips.
//cut deletes the blocks while copying.
//move only copies the selection without deleting the blocks. The first time it is pasted, the blocks are removed.
//paste reads a schematic file and sets blocks through the cassette chain.
Command senders with a certain permission may declare specific zones in the world as ”construction zones”. Builder sessions are granted with construction access in certain construction zones, only in which they can execute world-editing operations.
This limit may be bypassed through a specific command, or it can be set in the plugin configuration to mark the whole level/all levels as construction zones.
If a world-editing operation overlaps non-construction zones, the ignored blocks will be saved into a new cassette that can be managed later.
Builder sessions based on players can use a //sandbox prefix before their commands (e.g. //sandbox set snow such that all world-editing operations they execute are only temporarily visible on their client side, but do not update the level and are not visible to other players.
Builder sessions may also create named ”shared sandboxes”, inviting certain players (not related to builder sessions) to it and making world-editing operations they execute also visible to the invited players’ client side. This feature can be used (abused) to create automated games like spleef matches.
Since the changes are only temporary, it is not necessary to store the changes to a cassette. As a result, the chain of a sandbox operation only consists of four steps:
Sandbox operations are also affected by construction zones. Since no cassettes are involved, the unchanged blocks will not be stored, but they will be counted and reported. In addition, the block changer may return different blocks due to randomness (unlike redoing operations).