We're seeing an increased need to store "prefabs" - little areas with predefined block contents, such as village houses or fortress rooms - in collections. We have one collection of village houses for the plains village, one collection for the desert village, one collection for the nether fortress... And there are plans in the future to use even more collections - trees, overworld fortresses, more village types and even custom structures. The point that they have in common is that they need to store not only the prefabs, but also metadata for those prefabs - how often they generate, how they connect together. There's even need for metadata for the entire collection, such as what the accepted biomes are, what block should village roads use, and various other generator parameters. So we need a file format that could store all this information together.
There are some existing formats available to consider first:
With Lua, we could store any metadata for the prefabs, any additional information related to the entire set of prefabs. There's nothing stopping us from adding more items in a backward- and forward-compatible way. The prefabs can be stored very similar to the cpp format, an array of strings plus a charmap, or they can be stored externally in individual .schematic files and the Lua file would provide just the metadata. The server has already vast amounts of Lua-related support code that can be used for accessing the data. In the end this became the chosen solution. The format has been named "Cubeset" - a set of cube-based prefabs.
The Cubeset file has a .cubeset extension. Internally it is a Lua source file that provides a global value, Cubeset, which is a table containing the structured data. The loader checks the file's first 4 KiB to see if there is a "CubesetFormatVersion =" string in it, and if it is, the file is considered a Cubeset file and further loading is attempted. It is therefore crucial that tools producing this file format include this string as early as possible in the file.
The top-level Cubeset table must contain at least two sub-tables: Metadata and Pieces. The Metadata table contains the metadata relevant to the entire set of prefabs in the file, the Pieces table contains the definitions and metadata for the individual prefabs. It is advised to make the Metadata table the first one, because it contains the signature used to identify the file ("CubesetFormatVersion ="). Apart from these two subtables the server ignores everything else.
The Cubeset.Metadata table is used to store metadata for the entire set of prefabs, and also for the format and version identification. It is a regular dictionary-style Lua table; the following elements are recognized:
Name | Type | Content |
---|---|---|
CubesetFormatVersion | number | This is the format identification and at the same time it specifies the version of the file. Currently the file version is 1. |
ExportDate | string | The date when this file was exported, in the ISO format ("2015-06-16 13:34:03"). Inserted by GalExport for versioning purposes. Ignored elsewhere. |
ExternalSchematic | boolean | Flag inserted by GalExport to indicate that the individual prefabs are stored in separate .schematic files. Ignored elsewhere. |
The Cubeset.Pieces table is an array containing individual prefabs. Each element describes a single prefab and its associated metadata. The following values are recognized:
Name | Type | Content |
---|---|---|
OriginData | table | Inserted by GalExport to identify the gallery area from which the prefab is exported. Ignored elsewhere. |
Hitbox | table | The relative coords of the prefab's hitbox (where the prefab is not allowed to overlap another prefab when generating). Members: MinX, MinY, MinZ, MaxX, MaxY, MaxZ, all numbers. |
Connectors | table | Array of connector definitions. The table must be present for each prefab, even if the prefab doesn't have any connectors (use an empty table, then). |
SchematicFileName | string | Name of the .schematic file that contains the block data for the prefab. |
Size | table | Table containing the dimensions of the prefab, if it is inlined into the BlockData element. Contains three members, x, y, z, each is a number. |
BlockData | table | Array of strings that are processed to produce the block data for the prefab. Each letter in the string corresponds to one block, the type of the block is translated through the BlockDefinitions table. The blocks are ordered YZX, that is, the X coord varies the most. |
BlockDefinitions | table | Array of strings that defines the mapping of each letter in BlockData to a specific blocktype. Each string should have the format "Letter: BlockType: BlockMeta". |
Metadata | table | Dictionary-style table of various per-prefab metadata values. |
The connector definitions table is an array of tables, each element describing one connector. The following values are recognized:
Name | type | Content | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Type | number | The connector's type. The piece generator will only connect the connectors of inverse types ('1'-type connector will connect only to '-1'-type connector). | ||||||||||||||
RelX | number | X coord of the connector, relative to the prefab's zero point ({0, 0, 0} - the first block in the image). | ||||||||||||||
RelY | number | Y coord of the connector, relative to the prefab's zero point ({0, 0, 0} - the first block in the image). | ||||||||||||||
RelZ | number | Z coord of the connector, relative to the prefab's zero point ({0, 0, 0} - the first block in the image). | ||||||||||||||
Direction | number | The direction in which the connector is facing. Corresponds to the eBlockFace constants:
|
Each piece contains additional metadata describing its properties. The server ignores metadata that it doesn't understand. The following values are recognized:
Name | Type | IsRequired | Contents |
---|---|---|---|
IsStarting | number | Yes | Zero means that the piece is a regular piece, nonzero means that the piece is a starting piece (the "seed" of the structure). Required even for cubesets that don't represent a piece-generator data (such as trees). |
AllowedRotations | number | Number representing a bitmask for which rotations the piece supports. Defaults to 0 (no rotations). Bit 0 (value 1) represents whether 1 counter-clockwise rotation is allowed, bit 1 (value 2) represents whether 2 rotations (180 degrees) are allowed, bit 2 (value 4) represents whether 1 clockwise rotation is allowed. | |
AddWeightIfSame | number | How much weight (chance to generate) should the piece generator add to this piece when the parent piece is the same. It is possible to have negative values, meaning that the piece doesn't like repeating itself. Defaults to 0. | |
DefaultWeight | number | How much weight (chance to generate) does the piece have by default, without any modifiers (AddWeightIfSame, DepthWeight). Defaults to 0. | |
DepthWeight | string | Override for DefaultWeight for specific depth (in the tree used by the piece generator). String in the format "Depth1:Weight1|Depth2:Weight2|...". Each unlisted depth gets the DefaultWeight. Defaults to empty string (no override). | |
MergeStrategy | string | Which merge strategy should be used when drawing the prefab into the world. String representation of one of the cBlockArea:eMergeStrategy constants: "msOverwrite", "msFillAir", "msImprint", "msLake", "msSpongePrint", "msDifference", "msSimpleCompare", "msMask". Defaults to "msSpongePrint". | |
MoveToGround | number | Zero means that the piece will stay where generated by the piece generator, nonzero means that the piece will be moved Y-wise so that its first connector will be on the top block of the existing terrain. Useful for village houses. Defaults to 0. | |
ShouldExpandFloor | number | Nonzero means that the prefab's lowest slice will be repeated downwards until it hits a solid block, effectively creating a foundation for the piece. Useful for nether fortresses and village houses. Defaults to 0. |
The following example defines a cubeset with two pieces. The first piece is inlined into the cubeset file, the second piece uses an external schematic file.
Cubeset = { Metadata = { CubesetFormatVersion = 1, }, Pieces = { -- The following piece was exported from the Gallery server by the GalExport plugin in the "cubeset" format: { OriginData = { ExportName = "DarkCorridor", Name = "Nether 3", GalleryName = "Nether", GalleryIndex = "3", ID = "30", CreatorName = "STR_Warrior", }, Size = { x = 14, y = 6, z = 5, }, Hitbox = { MinX = 0, MinY = 0, MinZ = 0, MaxX = 13, MaxY = 5, MaxZ = 4, }, Connectors = { { Type = 1, RelX = 0, RelY = 1, RelZ = 2, Direction = 4, -- X- }, { Type = 1, RelX = 13, RelY = 1, RelZ = 2, Direction = 5, -- X+ }, { Type = -1, RelX = 0, RelY = 1, RelZ = 2, Direction = 4, -- X- }, { Type = -1, RelX = 13, RelY = 1, RelZ = 2, Direction = 5, -- X+ }, }, Metadata = { ["DefaultWeight"] = "100", ["IsStarting"] = "0", ["AllowedRotations"] = "7", ["MergeStrategy"] = "msSpongePrint", ["DepthWeight"] = "", ["ShouldExpandFloor"] = "1", ["MoveToGround"] = "0", ["AddWeightIfSame"] = "0", }, BlockDefinitions = { ".: 0: 0", -- air "a:112: 0", -- netherbrick "b:113: 0", -- netherbrickfence "c:114: 2", -- netherbrickstairs "d:114: 3", -- netherbrickstairs "m: 19: 0", -- sponge }, BlockData = { -- Level 0 "aaaaaaaaaaaaaa", -- 0 "aaaaaaaaaaaaaa", -- 1 "aaaaaaaaaaaaaa", -- 2 "aaaaaaaaaaaaaa", -- 3 "aaaaaaaaaaaaaa", -- 4 -- Level 1 "aaaaaaaaaaaaaa", -- 0 "..............", -- 1 "..............", -- 2 "..............", -- 3 "aaaaaaaaaaaaaa", -- 4 -- Level 2 "aabaaaaaaaabaa", -- 0 "..............", -- 1 "..............", -- 2 "..............", -- 3 "aabaaaaaaaabaa", -- 4 -- Level 3 "aabaaaaaaaabaa", -- 0 "..............", -- 1 "..............", -- 2 "..............", -- 3 "aabaaaaaaaabaa", -- 4 -- Level 4 "aabaaaaaaaabaa", -- 0 "..............", -- 1 "..............", -- 2 "..............", -- 3 "aabaaaaaaaabaa", -- 4 -- Level 5 "cccccccccccccc", -- 0 "aaaaaaaaaaaaaa", -- 1 "aaaaaaaaaaaaaa", -- 2 "aaaaaaaaaaaaaa", -- 3 "dddddddddddddd", -- 4 }, }, -- DarkCorridor -- The following piece was exported from the Gallery server by the GalExport plugin in the "cubesetext" format: { OriginData = { ExportName = "DoublePlantBed", Name = "Plains 5", GalleryName = "Plains", GalleryIndex = "5", ID = "20", CreatorName = "tonibm1999", }, Size = { x = 15, y = 8, z = 9, }, Hitbox = { MinX = 0, MinY = 0, MinZ = 0, MaxX = 14, MaxY = 7, MaxZ = 8, }, Connectors = { { Type = -1, RelX = 7, RelY = 2, RelZ = 8, Direction = 3, -- Z+ }, }, Metadata = { ["DefaultWeight"] = "100", ["IsStarting"] = "0", ["AllowedRotations"] = "7", ["MergeStrategy"] = "msSpongePrint", ["DepthWeight"] = "", ["ShouldExpandFloor"] = "1", ["MoveToGround"] = "1", ["AddWeightIfSame"] = "0", }, SchematicFile = "PlainsVillage/20.schematic", }, -- DoublePlantBed } }