Introduction

Luanti is a voxel game engine written in C++ that allows you to mod it with Lua.
Games 🕹️
When you start Luanti for the first time it will tell you to download a game. A game is necessary to generate worlds.
Some famous games made with this engine are Minetest the game, VoxeLibre and NodeCore
Some little pearls are Void, Cavegame and Colorlandia
Servers
Some famous servers are Your Land, Capture the Flag and SquareOne
See the server list here servers.luanti.org or see the Epic Server List
Make your server for free with playit.gg with this guide
Nodes and entities
Luanti worlds are made of blocks or voxels, luanti calls them nodes. They are static elements and can have different shapes "drawtype"
Entities are objects that aren't attached to a grid position like nodes. Typically for objects that move like NPCs
Modding basics
Go to your luanti folder and get into /mods create a new folder mymod, inside create two files:
mod.conf
name = mymod
init.lua
--Here goes the code
Modding with Lua
There are some functions to do what you want to do. They are called Global callback registration functions
Salute the player:
core.register_on_joinplayer(function(player, last_login) local playername = player:get_player_name() core.chat_send_player(playername, "Hi " .. playername .. "!") end)
♪ Play a sound when a player joins
Have a welcomesound.ogg file in /sounds folder
local music_file = "welcomesound" local sound = nil core.register_on_joinplayer(function(player, last_login) sound = core.sound_play(music_file, { loop = false, gain = 0.5 , -- Adjust volume name = "welcoming sound", -- Identifier for the sound }) end)
Create new nodes
core.register_node('blank:grass', { description = 'Beautiful grass node', tiles = { 'grass.png' }, groups = { dig_immediate = 3 }, is_ground_content = true })
Use /giveme blank:grass to get the node.
Create your game
To start from zero we will create our own game.
We need to go to our luanti installation path and enter /games. Create a folder, for example: "Blank".
Inside of it we will create a file called game.conf
title = Blank author = dibesfer description = Absolute blank template to start your projects from. disallowed_mapgens = v6
Also we will create a /mods folder
Inside of it we will create the main mod we will just call blank
Inside of /blank we will create a mod.conf and init.lua files.
mod.conf (name = blank)
init.lua we will work upon of this.
We will also create a folder /textures for storing our assets.
Create A Full Biome

Want to make your own voxel world like Minecraft? follow this short steps guide with images
Objectives:
- Create a node (stone, water, river_water)
- Create a biome (grass, dirt)
- Create a plant (flower, long plant)
- Create an ore (ruby, sapphire, emerald, mese)
- Create an schematic (tree)
Create your first node
In order to create worlds with most map generators like v7, valleys, flat... Luanti needs to set three essential nodes: stone, water and river water.



core.register_node('blank:stone', { description = 'Stone', tiles = { 'stone.png' }, groups = { dig_immediate = 3 }, paramtype = "light" }) core.register_node('blank:water', { description = 'Water', tiles = { 'water.png' }, groups = { dig_immediate = 3 }, paramtype = "light" }) core.register_node('blank:river_water', { description = 'River Water', tiles = { 'river_water.png' }, groups = { dig_immediate = 3 }, paramtype = "light" })
Now we register the aliases
core.register_alias('mapgen_stone', 'blank:stone') core.register_alias('mapgen_water_source', 'blank:water') core.register_alias('mapgen_river_water_source', 'blank:river_water')
Mapgen v6 needs a lot more of essential nodes like lava so we will skip it to keep it simple.
With these nodes we will have a world like this (if we select Valleys mapgen)

Let's paint it green adding some grass.
Create a biome ⛰️
Lets create a grass and dirt node.


core.register_node('blank:grass', { description = 'Grass', tiles = { 'grass.png' }, groups = { dig_immediate = 3 }, paramtype = "light", }) core.register_node('blank:dirt', { description = 'Dirt', tiles = { 'dirt.png' }, groups = { dig_immediate = 3 }, paramtype = "light", })
Register the biome
core.register_biome({ name = "grassland", node_top = "blank:grass", depth_top = 1, node_filler = "blank:dirt", depth_filler = 4, node_riverbed = "blank:river_water", node_stone = "blank:stone", node_water = "blank:water", depth_riverbed = 2, y_max = 31000, y_min = -100, heat_point = 10, humidity_point = 10, })
Now we have this (v7 mapgen)

Create a flower 🌺
Let's create two vegetations, flower and long plant.



core.register_node("blank:flower", { description = "Blank Flower", drawtype = "plantlike", waving = 1, tiles = { "flower.png"}, inventory_image = "flower.png", wield_image = "flower.png", sunlight_propagates = true, paramtype = "light", walkable = false, groups = { dig_immediate = 3}, })
Register a decoration
core.register_decoration({ name = "blank_flower", deco_type = "simple", place_on = {"blank:grass"}, sidelen = 16, fill_ratio = 0.005, y_max = 31000, y_min = 0, decoration = { "blank:blank_flower"}, })
Now we see the flowers spawning in the world!

Create and register an ore 💎
Let's create ruby, emerald, sapphire and mese




core.register_node('blank:ruby', { description = 'Ruby', tiles = { 'ruby.png' }, groups = { dig_immediate = 3 }, is_ground_content = true })
Register it
core.register_ore({ ore_type = "scatter", ore = "blank:ruby", wherein = "blank:stone", clust_scarcity = 8 * 8 * 8, clust_num_ores = 16, clust_size = 16, y_max = 31000, y_min = -127, })
Now we see the ores. Plenty of ores

Trees 🌲
Create wood and leaves, create a tree, create a schematic, register a decoration


core.register_node('blank:wood', { description = 'Wood', tiles = { 'wood.png' }, groups = { dig_immediate = 3 }, }) core.register_node('blank:leaves', { description = 'Leaves', tiles = { 'leaves.png' }, groups = { dig_immediate = 3 }, })
Go in-game an build a tree.
Download Worldedit mod and enable it on your world.
Build a tree and select the opposite vertices of an imaginary box.
We set first position with //1 and second position with //2

Save an schematic writing //mtschemcreate tree
Now navigate to your world path and enter the schems folder.
Take that file and put it into a new folder inside your mod called schematics.
Register the decoration
core.register_decoration({ name = "tree", deco_type = "schematic", place_on = {"blank:grass"}, place_offset_y = 1, sidelen = 16, fill_ratio = 0.001, biomes = "normalland", y_max = 31000, y_min = 0, schematic = core.get_modpath("blank") .. "/schematics/tree.mts", flags = "place_center_x, place_center_z", rotation = "random", })
We use place_offset_y=1 because by default Luanti places decorations at ground level.
Now trees will spawn all over the place

Blank
Blank is a minimal template. It was made along with this tutorial.
Blank contains only 11 nodes and 1 item the "" hand
grass, stone, dirt, ruby, sapphire, mese, water, river water, flower, wood, leaves
You can download it from Luanti ContentDB here
Colorize your messages
Let's store this info in mod storage and create a variable for color
local storage = core.get_mod_storage() default_color = "#FFFFFF" -- White
Function to get a player's saved color
local function get_player_color(player_name) return storage:get_string(player_name) ~= "" and storage:get_string(player_name) or default_color end
Command to set chat color
core.register_chatcommand("setcolor", { params = "#RRGGBB", description = "Set your chat color", func = function(name, param) if param:match("^#%x%x%x%x%x%x$") then storage:set_string(name, param) return true, "Chat color set to " .. param else return false, "Invalid color format! Use rgb #RRGGBB" end end, })
Modify player messages
core.register_on_chat_message(function(name, message) local color = get_player_color(name) core.chat_send_all(core.colorize(color, "<" .. name .. "> " .. message)) return true end)
return true stops default chat handling. Now use /setcolor #ff0000 to turn the text red.
Use a resource like rgbcolorpicker.com to get your color rgb code!

Textures
Tired of boring plain textures?
Use gimp to apply Filter > Noise > HSV Noise


Now the world looks like this

Colorize Textures
Let's create a plain black and white (or grayscale) texture image.

Use the texture modifier ^[colorize:#ff0000:180 to set a RGB color red and a ratio: 0-only the texture to 255-only the color
core.register_node('blank:wool', { description = 'Wool', tiles = { 'wool.png' }, groups = { dig_immediate = 3 }, paramtype = "light", }) core.register_node('blank:red_wool', { description = 'Wool', tiles = { "wool.png^[colorize:#ff0000:180" }, groups = { dig_immediate = 3 }, paramtype = "light", })
Use colors like green #00FF00 or blue #0000FF

Gallery
Let's get creative

Meet the player (change camera model with C)



Future Tutorials 🎓
- Create an entity
- Create a recipe
- Create a tool
Credits
Author: dibesfer - dibesfer@gmail.com
Coded with HTML, CSS and javascript on VSCodium
Host: github pages
luanti - celeron55 (Perttu Ahola) celeron55@gmail.com
Fonts: Ubuntu and Ubuntu Mono
Stats
Local visits:
Total visits: