Quick Nav


Back To Top

Introduction

In addition to the standard "base" configs that simply control mod-wide options, many of my mods use the "LuaBlock" system for defining more complex objects to be used by some part of the mod. This can range from simple definitions, such as custom Void Monster drops, to slightly more complex versions that are effectively arrays of data - such as CaveControl biome definitions - to complex structures with nested tables, such as for Satisforestry biome configs or custom RotaryCraft machine recipes.

The name "LuaBlock" comes from the fact that the structure and format of an entry is designed to resemble Factorio prototype definitions, which are themselves nested Lua tables. However, despite this name, and the common use of .lua as the file extension for default or generated LuaBlock files (done to prompt syntax highlighting in editors), they are not intended or parsed as actual lua code. That said, it is semantically quite similar, meaning if you are familiar with Lua table objects, this system should come naturally. For most use cases, the actual file extension is flexible; .lua is the default (and is recommended for the highlighting described above), but most LuaBlock-using systems will also look for .txt, .cfg, .ini, .yml, or similar "config text file" type file extensions.

I developed this system in December 2015 for CondensedOres, as that mod required a way to easily define "ore generation entries", ie a set of values which together defined an ore generation template, including data such as what blocks to use, the spawn rules, the frequencies, and the height profile. The standard configs, being one global list of values, was not suited for this, nor was the old "comma separated string" I used in some old "custom entry" configs (eg RC extractor ores and TerritoryZone definitions). Since then, more and more of my mods have started using it, from CaveControl switching its definition format to LuaBlock, to new systems for custom recipes in RotaryCraft, its addons, and ChromatiCraft, to entirely new mods like LootTweaks and Satisforestry being designed with it from the beginning.

Format

LuaBlock is conceptually quite simple; it is simply a way of specifying assorted key-value pairs, where the value may in turn be another table of such pairs. For example, take the following LuaBlock definition:
{
type = "somename"
amount = 24
fluid = "water"
is_cold = true
colors = {
0x0000ff
0x22aaff
0x60a0ff
}
}

This defines a table of six key-value pairs. Two (type and fluid) are mapped to string values, one to an integer, one to a boolean, and one to a list (which itself contains three integers specified in hexadecimal format). Whatever system is expecting this kind of definition would presumably apply that information - fetching values by key as needed - to something for which those properties were relevant. For example, that might define a block with three color variants, a temperature hot/cold, and an amount of some fluid type.

Details

The format is always like in the example above; one key-value pair per line (not counting cases where the value spans multiple lines), with no separating commas or other delimiters, formatted as "key = value" (tabs and spaces are ignored, but recommended to aid readability). The type of variable used for the value will be based on whichever valid format it matches - "true" and "false" (ignoring case) will become booleans; any integer will become as such; any other number will become a double-precision floating point value; anything else becomes a String. You can specify a 'type override' for values which "look" like one type but need to be interpreted as another, or for primitive types not chosen by default. To do this, specify the value in the format [datatype=type]value, where "value" is the value as normal and "type" is the Java primitive type you wish to use (byte, short, long, etc).

Items

Basic Definition

ItemStacks - like what are used in recipes or as drops - are defined somewhat specially, but similar to Minetweaker, in a format of modid:itemregistryname:metadata*stacksize. Metadata is optional, and will be set to zero if unspecified. Stack size is also optional (skip the * if not specifying it), and will default to one. If NBT is desired, a separate entry - usually called "nbt" or similar - will be parsed into the NBT tag using the same structure as the LuaBlock definition, with simple properties becoming the appropriate primitive tag types (NBTTagInt, NBTTagString, et cetera), lists of nameless values becoming an NBTTagList, and a series of named key-value pairs becoming a NBTTagCompound. Generally, the root NBT block will be your root NBTTagCompound on the item.

ItemStack Registries

Additionally, the "cached ItemStack" registry, which contains references to most crafting or resource type items in the applicable mod, and is found in several of my mods - such as ItemStacks in RotaryCraft and ChromaStacks in ChromatiCraft, can be directly queried to save looking for - often long and complicated - registry names. For example, this block will define a basic 3x3 crafting recipe:
{
type = "test1"
recipe = {
shaped = true
input_top = "ore:stone, ore:stone, null"
input_middle = "minecraft:gold_ingot, chromaticraft_stack:elementUnit, null"
input_bottom = "ore:ingotGold, null, rotarycraft_stack:steelgear"
}
output = "chromaticraft_stack:lumaDust*3"
output_tag = {
tag1 = 370
tag2 = "potato"
}
}

That recipe will take a grid of:
[stone, stone, empty]
[gold ingot, CC elemental core, empty]
[gold ingot, empty, steel gear]
And make a stack of 3 CC luma dust, with NBT consisting of two tags, "tag1" mapped to int 370, and "tag2" mapped to string "potato".

Item Delegates

For ease of use, some complex items have special handlers you can use to define them. Notably, this includes Forestry genetics items - saplings, bees, et cetera - and mystcraft pages. To use this, you specify a "delegate" tag as the item ID, and provide a fake NBT block containing whatever values define the item you want.

For example, these three blocks define a forestry white willow sapling, a steadfast princess with some specific genes, and the "Single Biome" mystcraft page.
{
item = "delegate:forestry_tree"
nbt = {
class = "SAPLING"
species = "forestry.treeWillow"
}
}
{
item = "delegate:forestry_bee"
nbt = {
class = "PRINCESS"
species = "forestry.speciesSteadfast"
tolerant_flyer = true
speed = "forestry.speedSlower"
}
}
{
item = "delegate:myst_page"
nbt = {
page = "BioConSingle"
}
}

Do note that you will need to know their internal IDs/names in their respective registries (for example the allele UID for most forestry genes, seen in the "species" tags above), but this can usually be determined by reading the NBT of a known valid item.

Special Keys

The "type" key is special - it is internally used as a lookup ID, and as such A) must always be present B) must be unique within its "domain" (ie whatever system this LuaBlock is being loaded into, such as CaveControl definitions or Satisforestry lizard doggo drops) and C) cannot be inherited from a parent. Its actual value is rarely - but not never - important; it is merely required that it be usable as an identifier. Additionally, should the parsing of the block fail, or the block be deemed an invalid definition (for example missing a required property), the value of "type" will be used to identify it in the log messages indicating this failure.

Another special key is "inherit"; this one is not mandatory, but it specifies the type ID (ie the value of "type") of the "parent" LuaBlock definition, used for inheritance.

Inheritance

One of the most convenient and powerful features of the LuaBlock system is that of inheritance, something also modelled on the Factorio prototypes. Every block can inherit from a parent, which allows for easier creation of "mostly the same but changing a few things" sort of definitions. For example, in an ore definition system, you may have 22 entries which all share the exact same spawn block, biome rules, and so on; you can specify those in a "base" definition, which the child definitions all inherit from, allowing them to only have to specify their unique properties (eg block lists).

For example, take the following three definitions, as they might appear in some file:
{
type = "basevalue"
rate = 13.5
color = 0xff0000
spawnRule = {
require = {
0
1
-1
}
exclude = {
7
-112
}
}
}
{
type = "defA"
inherit = "basevalue"
color = 0xffff00
minHeight = 32
}
{
type = "defB"
inherit = "basevalue"
rate = 105.2
minHeight = 60
}

Because defA and defB both inherit from basevalue, they acquire the properties within it that they do not explicitly specify. So both inherit the spawnRule entry (saving a lot of copy-paste of a complex value); additionally, defA inherits the rate of 13.5, and defB inherits the color (red).

Domains/Trees

Internally, LuaBlocks are loaded into various "trees", each being intended for a specific purpose, and generally (but not always) defined in a single file (or set of files in a folder). For example, when RotaryCraft is looking for custom Blast Furnace recipes, it looks to a specific file and loads every block it can find in that file to the same tree (as children under an implicit "root" block). This tree is the "domain" in which those LuaBlocks are used, and determines A) the "scope" of where their "type" value needs to be unique (it is no consequence if both an RC centrifuge recipe, a Satisforestry resource node definition, and a cave control biome rule all share the same type, as no system ever checks all three of those trees) and B) the "semantic" meaning of those entries, ie what keys are required and what each key "means". Internally, each "system" loading a LuaBlock tree is meant for a specific purpose (such as all the examples mentioned thus far); there is no sharing of data.

Troubleshooting

Because LuaBlock is ultimately a freeform text format, it is entirely possible for it to be specified incorrectly, such that the game will not load the entries and do with them as you intend. This can be either due to structural issues, or semantic incorrectness. The former is simply "the LuaBlock could not be parsed into a key-value map", and is a result of things like messing up the actual definition structure (putting multiple things on one line, adding extra characters, et cetera). Semantic incorrectness is far more common; that means that the block you defined is logically invalid in some way. For example, a recipe-defining block with no output item specified (or a specification for an item that does not exist) will count as invalid. The exact requirements and limitations is domain-specific; for example, CondensedOres entries must have (or inherit) things like heightRule and blocks, since an ore definition with no block list to use as the ore, or no Y values to generate at, is meaningless. Those properties would meanwhile be meaningless in almost any other context.

Note that superfluous properties do not cause any kind of failure; they simply get added into the internal map but never inspected. For example, you can safely define a key-value pair of, say, has_entity = true anywhere, but unless the operative domain actually looks for a property called "has_entity", it will be completely ignored.

When a block is failed to be loaded, or is deemed invalid, this is generally noted in the log file; the mod in question will say something to the effect of "definition not loaded" and give some kind of reason why (eg "temperature out of bounds"). Many mods will also indicate how many entries it found in the config, how many it attempted to load, and how many of those succeeded.