Merge pull request 'master' (#1) from Mirrorlandia_minetest/minetest:master into master

Reviewed-on: #1
This commit is contained in:
Bruno Rybársky 2023-11-24 22:11:16 +01:00
commit ff473180db
105 changed files with 32410 additions and 36185 deletions

@ -177,7 +177,6 @@ jobs:
run: |
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin32.sh winbuild
env:
NO_MINETEST_GAME: 1
NO_PACKAGE: 1
win64:
@ -195,15 +194,14 @@ jobs:
run: |
EXISTING_MINETEST_DIR=$PWD ./util/buildbot/buildwin64.sh winbuild
env:
NO_MINETEST_GAME: 1
NO_PACKAGE: 1
msvc:
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2019
env:
VCPKG_VERSION: 5cf60186a241e84e8232641ee973395d4fde90e1
# 2022.02
VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50
# 2023.10.19
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry
strategy:
fail-fast: false

@ -21,11 +21,6 @@ on:
- 'cmake/Modules/**'
- '.github/workflows/macos.yml'
env:
MINETEST_GAME_REPO: https://github.com/minetest/minetest_game.git
MINETEST_GAME_BRANCH: master
MINETEST_GAME_NAME: minetest_game
jobs:
build:
runs-on: macos-latest
@ -38,7 +33,6 @@ jobs:
- name: Build
run: |
git clone -b $MINETEST_GAME_BRANCH $MINETEST_GAME_REPO games/$MINETEST_GAME_NAME
git clone https://github.com/minetest/irrlicht lib/irrlichtmt --depth 1 -b $(cat misc/irrlichtmt_tag.txt)
mkdir build
cd build

@ -9,7 +9,6 @@ stages:
- deploy
variables:
MINETEST_GAME_REPO: "https://github.com/minetest/minetest_game.git"
CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
.build_template:
@ -120,8 +119,6 @@ package:appimage-client:
- mkdir AppDir
- cp -a artifact/minetest/usr/ AppDir/usr/
- cp -a clientmods AppDir/usr/share/minetest
- git clone $MINETEST_GAME_REPO AppDir/usr/share/minetest/games/minetest_game
- rm -rf AppDir/usr/share/minetest/games/minetest_game/.git
# Remove PrefersNonDefaultGPU property due to validation errors
- sed -i '/PrefersNonDefaultGPU/d' AppDir/usr/share/applications/net.minetest.minetest.desktop
script:

@ -262,9 +262,6 @@ if(RUN_IN_PLACE)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/textures/texture_packs_here.txt" DESTINATION "${SHAREDIR}/textures")
endif()
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game" DESTINATION "${SHAREDIR}/games/"
COMPONENT "SUBGAME_MINETEST_GAME" OPTIONAL PATTERN ".git*" EXCLUDE )
set(INSTALL_DEVTEST FALSE CACHE BOOL "Install Development Test")
if(INSTALL_DEVTEST)
@ -286,7 +283,7 @@ install(FILES "doc/lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/client_lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/menu_lua_api.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/texture_packs.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/world_format.txt" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "doc/world_format.md" DESTINATION "${DOCDIR}" COMPONENT "Docs")
install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}")
if(UNIX AND NOT APPLE)
@ -336,16 +333,6 @@ cpack_add_component(Docs
DESCRIPTION "Documentation about Minetest and Minetest modding"
)
cpack_add_component(SUBGAME_MINETEST_GAME
DISPLAY_NAME "Minetest Game"
DESCRIPTION "The default game bundled in the Minetest engine. Mainly used as a modding base."
GROUP "Games"
)
cpack_add_component_group(Subgames
DESCRIPTION "Games for the Minetest engine."
)
if(WIN32)
# Include all dynamically linked runtime libraries such as MSVCRxxx.dll
include(InstallRequiredSystemLibraries)

@ -1,7 +1,6 @@
ARG DOCKER_IMAGE=alpine:3.16
FROM $DOCKER_IMAGE AS dev
ENV MINETEST_GAME_VERSION master
ENV IRRLICHT_VERSION master
ENV SPATIALINDEX_VERSION 1.9.3
ENV LUAJIT_VERSION v2.1
@ -52,18 +51,16 @@ COPY src /usr/src/minetest/src
COPY textures /usr/src/minetest/textures
WORKDIR /usr/src/minetest
RUN git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
rm -fr ./games/minetest_game/.git && \
cmake -B build \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SERVER=TRUE \
-DENABLE_PROMETHEUS=TRUE \
-DBUILD_UNITTESTS=FALSE \
-DBUILD_CLIENT=FALSE \
-GNinja && \
cmake --build build && \
cmake --install build
RUN cmake -B build \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SERVER=TRUE \
-DENABLE_PROMETHEUS=TRUE \
-DBUILD_UNITTESTS=FALSE \
-DBUILD_CLIENT=FALSE \
-GNinja && \
cmake --build build && \
cmake --install build
ARG DOCKER_IMAGE=alpine:3.16
FROM $DOCKER_IMAGE AS runtime

@ -10,12 +10,6 @@ Minetest is a free open-source voxel game engine with easy modding and game crea
Copyright (C) 2010-2022 Perttu Ahola <celeron55@gmail.com>
and contributors (see source file comments and the version control log)
In case you downloaded the source code
--------------------------------------
If you downloaded the Minetest Engine source code in which this file is
contained, you probably want to download the [Minetest Game](https://github.com/minetest/minetest_game/)
project too. See its README.txt for more information.
Table of Contents
------------------

@ -54,7 +54,6 @@ android {
task prepareAssets() {
def assetsFolder = "build/assets"
def projRoot = rootDir.parent
def gameToCopy = "minetest_game"
// See issue #4638
def unsupportedLanguages = new File("${projRoot}/src/unsupported_language_list.txt").text.readLines()
@ -81,9 +80,6 @@ task prepareAssets() {
copy {
from "${projRoot}/fonts" include "*.ttf" into "${assetsFolder}/fonts"
}
copy {
from "${projRoot}/games/${gameToCopy}" into "${assetsFolder}/games/${gameToCopy}"
}
copy {
from "${projRoot}/textures" into "${assetsFolder}/textures"
}

@ -38,7 +38,6 @@ public class Utils {
public static boolean isInstallValid(@NonNull Context context) {
File userDataDirectory = getUserDataDirectory(context);
return userDataDirectory.isDirectory() &&
new File(userDataDirectory, "games").isDirectory() &&
new File(userDataDirectory, "builtin").isDirectory() &&
new File(userDataDirectory, "client").isDirectory() &&
new File(userDataDirectory, "textures").isDirectory();

@ -27,6 +27,8 @@ core.features = {
get_light_data_buffer = true,
mod_storage_on_disk = true,
compress_zstd = true,
sound_params_start_time = true,
physics_overrides_v2 = true,
}
function core.has_feature(arg)

@ -698,17 +698,14 @@ local function resolve_auto_install_spec()
local resolved = nil
for _, pkg in ipairs(store.packages_full_unordered) do
if pkg.author == auto_install_spec.author and
(pkg.name == auto_install_spec.name or
(pkg.type == "game" and pkg.name == auto_install_spec.name .. "_game")) then
if pkg.id == auto_install_spec then
resolved = pkg
break
end
end
if not resolved then
gamedata.errormessage = fgettext("The package $1/$2 was not found.",
auto_install_spec.author, auto_install_spec.name)
gamedata.errormessage = fgettext("The package $1 was not found.", auto_install_spec)
ui.update()
auto_install_spec = nil
@ -777,26 +774,26 @@ function store.update_paths()
local mod_hash = {}
pkgmgr.refresh_globals()
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
if mod.author and mod.release > 0 then
local id = mod.author:lower() .. "/" .. mod.name
mod_hash[store.aliases[id] or id] = mod
local cdb_id = pkgmgr.get_contentdb_id(mod)
if cdb_id then
mod_hash[store.aliases[cdb_id] or cdb_id] = mod
end
end
local game_hash = {}
pkgmgr.update_gamelist()
for _, game in pairs(pkgmgr.games) do
if game.author ~= "" and game.release > 0 then
local id = game.author:lower() .. "/" .. game.id
game_hash[store.aliases[id] or id] = game
local cdb_id = pkgmgr.get_contentdb_id(game)
if cdb_id then
game_hash[store.aliases[cdb_id] or cdb_id] = game
end
end
local txp_hash = {}
for _, txp in pairs(pkgmgr.get_texture_packs()) do
if txp.author and txp.release > 0 then
local id = txp.author:lower() .. "/" .. txp.name
txp_hash[store.aliases[id] or id] = txp
local cdb_id = pkgmgr.get_contentdb_id(txp)
if cdb_id then
txp_hash[store.aliases[cdb_id] or cdb_id] = txp
end
end
@ -815,6 +812,7 @@ function store.update_paths()
package.installed_release = content.release or 0
else
package.path = nil
package.installed_release = nil
end
end
end
@ -1193,7 +1191,7 @@ end
--- @param type string | nil
--- Sets initial package filter. "game", "mod", "txp" or nil (no filter).
--- @param install_spec table | nil
--- Package specification of the form { author = string, name = string }.
--- ContentDB ID of package as returned by pkgmgr.get_contentdb_id().
--- Sets package to install or update automatically.
function create_store_dlg(type, install_spec)
search_string = ""

@ -777,6 +777,30 @@ function pkgmgr.update_gamelist()
end)
end
--------------------------------------------------------------------------------
-- Returns the ContentDB ID for an installed piece of content.
function pkgmgr.get_contentdb_id(content)
-- core.get_games() will return "" instead of nil if there is no "author" field.
if content.author and content.author ~= "" and content.release > 0 then
if content.type == "game" then
return content.author:lower() .. "/" .. content.id
end
return content.author:lower() .. "/" .. content.name
end
-- Until Minetest 5.8.0, Minetest Game was bundled with Minetest.
-- Unfortunately, the bundled MTG was not versioned (missing "release"
-- field in game.conf).
-- Therefore, we consider any installation of MTG that is not versioned,
-- has not been cloned from Git, and is not system-wide to be updatable.
if content.type == "game" and content.id == "minetest" and content.release == 0 and
not core.is_dir(content.path .. "/.git") and core.may_modify_path(content.path) then
return "minetest/minetest"
end
return nil
end
--------------------------------------------------------------------------------
-- read initial data
--------------------------------------------------------------------------------

@ -59,7 +59,7 @@ local function has_packages_from_cdb()
pkgmgr.update_gamelist()
for _, content in pairs(pkgmgr.get_all()) do
if content.author and content.release > 0 then
if pkgmgr.get_contentdb_id(content) then
return true
end
end
@ -114,18 +114,13 @@ function update_detector.get_all()
local ret = {}
local all_content = pkgmgr.get_all()
for _, content in ipairs(all_content) do
if content.author and content.release > 0 then
-- The backend will account for aliases in `latest_releases`
local id = content.author:lower() .. "/"
if content.type == "game" then
id = id .. content.id
else
id = id .. content.name
end
local cdb_id = pkgmgr.get_contentdb_id(content)
local latest_release = latest_releases[id]
if cdb_id then
-- The backend will account for aliases in `latest_releases`
local latest_release = latest_releases[cdb_id]
if not latest_release and content.type == "game" then
latest_release = latest_releases[id .. "_game"]
latest_release = latest_releases[cdb_id .. "_game"]
end
if latest_release and latest_release > content.release then

@ -78,7 +78,7 @@ local function buttonhandler(this, fields)
local maintab = ui.find_by_name("maintab")
local dlg = create_store_dlg(nil, { author = "Minetest", name = "minetest_game" })
local dlg = create_store_dlg(nil, "minetest/minetest")
dlg:set_parent(maintab)
maintab:hide()
dlg:show()

@ -27,7 +27,6 @@ local full_settings = settingtypes.parse_config_file(false, true)
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
local gettext = fgettext_ne
local all_pages = {}
local page_by_id = {}
local filtered_pages = all_pages
@ -66,23 +65,23 @@ local change_keys = {
add_page({
id = "accessibility",
title = gettext("Accessibility"),
title = fgettext_ne("Accessibility"),
content = {
"language",
{ heading = gettext("General") },
{ heading = fgettext_ne("General") },
"font_size",
"chat_font_size",
"gui_scaling",
"hud_scaling",
"show_nametag_backgrounds",
{ heading = gettext("Chat") },
{ heading = fgettext_ne("Chat") },
"console_height",
"console_alpha",
"console_color",
{ heading = gettext("Controls") },
{ heading = fgettext_ne("Controls") },
"autojump",
"safe_dig_and_place",
{ heading = gettext("Movement") },
{ heading = fgettext_ne("Movement") },
"arm_inertia",
"view_bobbing_amount",
"fall_bobbing_amount",
@ -97,7 +96,7 @@ local function load_settingtypes()
if not page then
page = add_page({
id = (section or "general"):lower():gsub(" ", "_"),
title = section or gettext("General"),
title = section or fgettext_ne("General"),
section = section,
content = {},
})
@ -117,13 +116,11 @@ local function load_settingtypes()
content = {},
}
if page.title:sub(1, 5) ~= "Hide:" then
page = add_page(page)
end
page = add_page(page)
elseif entry.level == 2 then
ensure_page_started()
page.content[#page.content + 1] = {
heading = gettext(entry.readable_name or entry.name),
heading = fgettext_ne(entry.readable_name or entry.name),
}
end
else

@ -1,5 +1,3 @@
local settings = ...
local concat = table.concat
local insert = table.insert
local sprintf = string.format
@ -36,7 +34,7 @@ local group_format_template = [[
]]
local function create_minetest_conf_example()
local function create_minetest_conf_example(settings)
local result = { minetest_example_header }
for _, entry in ipairs(settings) do
if entry.type == "category" then
@ -108,14 +106,11 @@ local translation_file_header = [[
fake_function() {]]
local function create_translation_file()
local function create_translation_file(settings)
local result = { translation_file_header }
for _, entry in ipairs(settings) do
if entry.type == "category" then
insert(result, sprintf("\tgettext(%q);", entry.name))
elseif entry.type == "key" then --luacheck: ignore
-- Neither names nor descriptions of keys are used since we have a
-- dedicated menu for them.
else
if entry.readable_name then
insert(result, sprintf("\tgettext(%q);", entry.readable_name))
@ -132,12 +127,13 @@ local function create_translation_file()
end
local file = assert(io.open("minetest.conf.example", "w"))
file:write(create_minetest_conf_example())
file:write(create_minetest_conf_example(settingtypes.parse_config_file(true, false)))
file:close()
file = assert(io.open("src/settings_translation_file.cpp", "w"))
-- If 'minetest.conf.example' appears in the 'bin' folder, the line below may have to be
-- used instead. The file will also appear in the 'bin' folder.
--file = assert(io.open("settings_translation_file.cpp", "w"))
file:write(create_translation_file())
-- We don't want hidden settings to be translated, so we set read_all to false.
file:write(create_translation_file(settingtypes.parse_config_file(false, false)))
file:close()

@ -25,4 +25,4 @@ dofile(path .. DIR_DELIM .. "dlg_settings.lua")
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
-- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
-- assert(loadfile(path .. DIR_DELIM .. "generate_from_settingtypes.lua"))(settingtypes.parse_config_file(true, false))
-- dofile(path .. DIR_DELIM .. "generate_from_settingtypes.lua")

@ -70,14 +70,37 @@ local function parse_setting_line(settings, line, read_all, base_level, allow_se
-- category
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
if category then
local category_level = stars:len() + base_level
if settings.current_hide_level then
if settings.current_hide_level < category_level then
-- Skip this category, it's inside a hidden category.
return
else
-- The start of this category marks the end of a hidden category.
settings.current_hide_level = nil
end
end
if not read_all and category:sub(1, 5) == "Hide:" then
-- This category is hidden.
settings.current_hide_level = category_level
return
end
table.insert(settings, {
name = category,
level = stars:len() + base_level,
level = category_level,
type = "category",
})
return
end
if settings.current_hide_level then
-- Ignore this line, we're inside a hidden category.
return
end
-- settings
local first_part, name, readable_name, setting_type = line:match("^"
-- this first capture group matches the whole first part,
@ -349,6 +372,7 @@ end
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
result.current_comment = {}
result.current_hide_level = nil
local line = file:read("*line")
while line do
@ -360,6 +384,7 @@ local function parse_single_file(file, filepath, read_all, result, base_level, a
end
result.current_comment = nil
result.current_hide_level = nil
end

@ -254,7 +254,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
if fields.btn_mod_mgr_update then
local pkg = packages:get_list()[tabdata.selected_pkg]
local dlg = create_store_dlg(nil, { author = pkg.author, name = pkg.id or pkg.name })
local dlg = create_store_dlg(nil, pkgmgr.get_contentdb_id(pkg))
dlg:set_parent(tabview)
tabview:hide()
dlg:show()

@ -26,7 +26,7 @@ For Fedora users:
For openSUSE users:
sudo zypper install gcc cmake libjpeg8-devel libpng16-devel openal-soft-devel libcurl-devel sqlite3-devel luajit-devel libzstd-devel
sudo zypper install gcc cmake libjpeg8-devel libpng16-devel openal-soft-devel libcurl-devel sqlite3-devel luajit-devel libzstd-devel Mesa-libGL-devel libXi-devel libvorbis-devel freetype2-devel
For Arch users:
@ -69,10 +69,6 @@ Download source (this is the URL to the latest of source repository, which might
git clone --depth 1 https://github.com/minetest/minetest.git
cd minetest
Download Minetest Game (otherwise only the "Development Test" game is available) using Git:
git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
Download IrrlichtMt to `lib/irrlichtmt`, it will be used to satisfy the IrrlichtMt dependency that way:
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt
@ -83,14 +79,6 @@ Download source, without using Git:
tar xf master.tar.gz
cd minetest-master
Download Minetest Game, without using Git:
cd games/
wget https://github.com/minetest/minetest_game/archive/master.tar.gz
tar xf master.tar.gz
mv minetest_game-master minetest_game
cd ..
Download IrrlichtMt, without using Git:
cd lib/

@ -20,12 +20,6 @@ git clone --depth 1 https://github.com/minetest/minetest.git
cd minetest
```
Download Minetest Game (otherwise only the "Development Test" game is available) using Git:
```
git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
```
Download Minetest's fork of Irrlicht:
```

@ -1091,6 +1091,7 @@ Table used to specify how a sound is played:
-- its end in `-start_time` seconds.
-- It is unspecified what happens if `loop` is false and `start_time` is
-- smaller than minus the sound's length.
-- Available since feature `sound_params_start_time`.
loop = false,
-- If true, sound is played in a loop.
@ -1629,7 +1630,7 @@ HUD
HUD element types
-----------------
The position field is used for all element types.
The `position` field is used for all element types.
To account for differing resolutions, the position coordinates are the
percentage of the screen, ranging in value from `0` to `1`.
@ -2148,11 +2149,13 @@ to games.
* `fall_damage_add_percent`: modifies the fall damage suffered when hitting
the top of this node. There's also an armor group with the same name.
The final player damage is determined by the following formula:
```lua
damage =
collision speed
* ((node_fall_damage_add_percent + 100) / 100) -- node group
* ((player_fall_damage_add_percent + 100) / 100) -- player armor group
- (14) -- constant tolerance
```
Negative damage values are discarded as no damage.
* `falling_node`: if there is no walkable block under the node it will fall
* `float`: the node will not fall through liquids (`liquidtype ~= "none"`)
@ -4006,9 +4009,9 @@ Translations
Texts can be translated client-side with the help of `minetest.translate` and
translation files.
Consider using the script `util/mtt_update.py` in the Minetest repository
to generate and update translation files automatically from the Lua sources.
See `util/README_mtt_update.md` for an explanation.
Consider using the script `util/mod_translation_updater.py` in the Minetest
repository to generate and update translation files automatically from the Lua
sources. See `util/README_mod_translation_updater.md` for an explanation.
Translating a string
--------------------
@ -4111,9 +4114,10 @@ On some specific cases, server translation could be useful. For example, filter
a list on labels and send results to client. A method is supplied to achieve
that:
`minetest.get_translated_string(lang_code, string)`: Translates `string` using
translations for `lang_code` language. It gives the same result as if the string
was translated by the client.
`minetest.get_translated_string(lang_code, string)`: resolves translations in
the given string just like the client would, using the translation files for
`lang_code`. For this to have any effect, the string needs to contain translation
markup, e.g. `minetest.get_translated_string("fr", S("Hello"))`.
The `lang_code` to use for a given player can be retrieved from
the table returned by `minetest.get_player_information(name)`.
@ -5268,6 +5272,12 @@ Utilities
mod_storage_on_disk = true,
-- "zstd" method for compress/decompress (5.7.0)
compress_zstd = true,
-- Sound parameter tables support start_time (5.8.0)
sound_params_start_time = true,
-- New fields for set_physics_override: speed_climb, speed_crouch,
-- liquid_fluidity, liquid_fluidity_smooth, liquid_sink,
-- acceleration_default, acceleration_air (5.8.0)
physics_overrides_v2 = true,
}
```
@ -7750,6 +7760,8 @@ child will follow movement and rotation of that bone.
settings (e.g. via the game's `minetest.conf`) to set a global base value
for all players and only use `set_physics_override` when you need to change
from the base value on a per-player basis
* Note: Some of the fields don't exist in old API versions, see feature
`physics_overrides_v2`.
* `get_physics_override()`: returns the table given to `set_physics_override`
* `hud_add(hud definition)`: add a HUD element described by HUD def, returns ID

627
doc/world_format.md Normal file

@ -0,0 +1,627 @@
# Minetest World Format 22...29
This applies to a world format carrying the block serialization version
22...27, used at least in
* `0.4.dev-20120322` ... `0.4.dev-20120606` (22...23)
* `0.4.0` (23)
* 24 was never released as stable and existed for ~2 days
* 27 was added in `0.4.15-dev`
* 29 was added in `5.5.0-dev`
The block serialization version does not fully specify every aspect of this
format; if compliance with this format is to be checked, it needs to be
done by detecting if the files and data indeed follows it.
# Files
Everything is contained in a directory, the name of which is freeform, but
often serves as the name of the world.
Currently, the authentication and ban data is stored on a per-world basis.
It can be copied over from an old world to a newly created world.
World
├── auth.txt ───── Authentication data
├── auth.sqlite ── Authentication data (SQLite alternative)
├── env_meta.txt ─ Environment metadata
├── ipban.txt ──── Banned IPs/users
├── map_meta.txt ─ Map metadata
├── map.sqlite ─── Map data
├── players ────── Player directory
│ │── player1 ── Player file
│ └── Foo ────── Player file
└── world.mt ───── World metadata
## `auth.txt`
Contains authentication data, one player per line.
<name>:<password hash>:<privilege1,...>
Legacy format (until 0.4.12) of password hash is `<name><password>` SHA1'd,
in base64.
Format (since 0.4.13) of password hash is `#1#<salt>#<verifier>`, with the
parts inside `<>` encoded in base64.
`<verifier>` is an RFC 2945 compatible SRP verifier,
of the given salt, password, and the player's name lowercased,
using the 2048-bit group specified in RFC 5054 and the SHA-256 hash function.
Example lines:
* Player "celeron55", no password, privileges "interact" and "shout":
```
celeron55::interact,shout
```
* Player "Foo", password "bar", privilege "shout", with a legacy password hash:
```
foo:iEPX+SQWIR3p67lj/0zigSWTKHg:shout
```
* Player "Foo", password "bar", privilege "shout", with a 0.4.13 password hash:
```
foo:#1#hPpy4O3IAn1hsNK00A6wNw#Kpu6rj7McsrPCt4euTb5RA5ltF7wdcWGoYMcRngwDi11cZhPuuR9i5Bo7o6A877TgcEwoc//HNrj9EjR/CGjdyTFmNhiermZOADvd8eu32FYK1kf7RMC0rXWxCenYuOQCG4WF9mMGiyTPxC63VAjAMuc1nCZzmy6D9zt0SIKxOmteI75pAEAIee2hx4OkSXRIiU4Zrxo1Xf7QFxkMY4x77vgaPcvfmuzom0y/fU1EdSnZeopGPvzMpFx80ODFx1P34R52nmVl0W8h4GNo0k8ZiWtRCdrJxs8xIg7z5P1h3Th/BJ0lwexpdK8sQZWng8xaO5ElthNuhO8UQx1l6FgEA:shout
```
* Player "bar", no password, no privileges:
```
bar::
```
## `auth.sqlite`
Contains authentication data as an SQLite database. This replaces auth.txt
above when `auth_backend` is set to `sqlite3` in world.mt.
This database contains two tables: `auth` and `user_privileges`:
```sql
CREATE TABLE `auth` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` VARCHAR(32) UNIQUE,
`password` VARCHAR(512),
`last_login` INTEGER
);
CREATE TABLE `user_privileges` (
`id` INTEGER,
`privilege` VARCHAR(32),
PRIMARY KEY (id, privilege),
CONSTRAINT fk_id FOREIGN KEY (id) REFERENCES auth (id) ON DELETE CASCADE
);
```
The `name` and `password` fields of the auth table are the same as the auth.txt
fields (with modern password hash). The `last_login` field is the last login
time as a Unix time stamp.
The `user_privileges` table contains one entry per privilege and player.
A player with "interact" and "shout" privileges will have two entries, one
with `privilege="interact"` and the second with `privilege="shout"`.
## `env_meta.txt`
Simple global environment variables.
Example content:
game_time = 73471
time_of_day = 19118
EnvArgsEnd
## `ipban.txt`
Banned IP addresses and usernames.
Example content:
123.456.78.9|foo
123.456.78.10|bar
## `map_meta.txt`
Simple global map variables.
Example content:
seed = 7980462765762429666
[end_of_params]
## `map.sqlite`
Map data.
See [Map File Format](#map-file-format) below.
## `player1`, `Foo`
Player data.
Filename can be anything.
See [Player File Format](#player-file-format) below.
## `world.mt`
World metadata.
gameid = mesetint - name of the game
enable_damage = true - whether damage is enabled or not
creative_mode = false - whether creative mode is enabled or not
backend = sqlite3 - which DB backend to use for blocks (sqlite3, dummy, leveldb, redis, postgresql)
player_backend = sqlite3 - which DB backend to use for player data
readonly_backend = sqlite3 - optionally read-only seed DB (DB file _must_ be located in "readonly" subfolder)
auth_backend = files - which DB backend to use for authentication data
mod_storage_backend = sqlite3 - which DB backend to use for mod storage
server_announce = false - whether the server is publicly announced or not
load_mod_<mod> = false - whether <mod> is to be loaded in this world
For `load_mod_<mod>`, the possible values are:
* `false` - Do not load the mod.
* `true` - Load the mod from wherever it is found (may cause conflicts if the same mod appears also in some other place).
* `mods/modpack/moddir` - Relative path to the mod
* Must be one of the following:
* `mods/`: mods in the user path's mods folder (ex. `/home/user/.minetest/mods`)
* `share/`: mods in the share's mods folder (ex. `/usr/share/minetest/mods`)
* `/path/to/env`: you can use absolute paths to mods inside folders specified with the `MINETEST_MOD_PATH` `env` variable.
* Other locations and absolute paths are not supported.
* Note that `moddir` is the directory name, not the mod name specified in mod.conf.
`PostgreSQL` backend specific settings:
pgsql_connection = host=127.0.0.1 port=5432 user=mt_user password=mt_password dbname=minetest
pgsql_player_connection = (same parameters as above)
pgsql_readonly_connection = (same parameters as above)
pgsql_auth_connection = (same parameters as above)
pgsql_mod_storage_connection = (same parameters as above)
`Redis` backend specific settings:
redis_address = 127.0.0.1 - Redis server address
redis_hash = foo - Database hash
redis_port = 6379 - (optional) Connection port
redis_password = hunter2 - (optional) Server password
# Player File Format
Should be pretty self-explanatory.
> **Note**: Position is in `nodes * 10`
Example content:
hp = 11
name = celeron55
pitch = 39.77
position = (-5231.97,15,1961.41)
version = 1
yaw = 101.37
PlayerArgsEnd
List main 32
Item default:torch 13
Item default:pick_steel 1 50112
Item experimental:tnt
Item default:cobble 99
Item default:pick_stone 1 13104
Item default:shovel_steel 1 51838
Item default:dirt 61
Item default:rail 78
Item default:coal_lump 3
Item default:cobble 99
Item default:leaves 22
Item default:gravel 52
Item default:axe_steel 1 2045
Item default:cobble 98
Item default:sand 61
Item default:water_source 94
Item default:glass 2
Item default:mossycobble
Item default:pick_steel 1 64428
Item animalmaterials:bone
Item default:sword_steel
Item default:sapling
Item default:sword_stone 1 10647
Item default:dirt 99
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
EndInventoryList
List craft 9
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
EndInventoryList
List craftpreview 1
Empty
EndInventoryList
List craftresult 1
Empty
EndInventoryList
EndInventory
# Map File Format
Minetest maps consist of `MapBlock`s, chunks of 16x16x16 nodes.
In addition to the bulk node data, `MapBlock`s stored on disk also contain
other things.
## History
Initially, Minetest stored maps in a format called the "sectors" format.
It was a directory/file structure like this:
sectors2/XXX/ZZZ/YYYY
For example, the `MapBlock` at `(0, 1, -2)` was this file:
sectors2/000/ffd/0001
Eventually Minetest outgrew this directory structure, as filesystems were
struggling under the number of files and directories.
Large servers seriously needed a new format, and thus the base of the
current format was invented, suggested by celeron55 and implemented by
JacobF.
`SQLite3` was implemented. Blocks files were directly inserted as blobs
in a single table, indexed by integer primary keys, which were hashed
coordinates.
Today, we know that `SQLite3` allows multiple primary keys (which would allow
storing coordinates separately), but the format has been kept unchanged for
that part.
## `map.sqlite`
`map.sqlite` is a `SQLite3` database, containing a single table, called
`blocks`. It looks like this:
```sql
CREATE TABLE `blocks` (`pos` INT NOT NULL PRIMARY KEY, `data` BLOB);
```
## Position Hashing
`pos` (a node position hash) is created from the three coordinates of a
`MapBlock` using this algorithm, defined here in Python:
```python
def getBlockAsInteger(p):
return int64(p[2]*16777216 + p[1]*4096 + p[0])
def int64(u):
while u >= 2**63:
u -= 2**64
while u <= -2**63:
u += 2**64
return u
```
It can be converted the other way by using this code:
```python
def getIntegerAsBlock(i):
x = unsignedToSigned(i % 4096, 2048)
i = int((i - x) / 4096)
y = unsignedToSigned(i % 4096, 2048)
i = int((i - y) / 4096)
z = unsignedToSigned(i % 4096, 2048)
return x,y,z
def unsignedToSigned(i, max_positive):
if i < max_positive:
return i
else:
return i - 2*max_positive
```
## Blob
The blob is the data that would have otherwise gone into the file.
See below for description.
# MapBlock Serialization Format
> **Notes**:
> * NOTE: Byte order is MSB first (big-endian).
> * NOTE: Zlib data is in such a format that Python's `zlib` at least can
> directly decompress.
> * NOTE: Since version 29 zstd is used instead of zlib. In addition, the entire
> block is first serialized and then compressed (except the version byte).
`u8` version
* map format version number, see serialization.h for the latest number
`u8` flags
* Flag bitmasks:
* `0x01`: `is_underground`: Should be set to 0 if there will be no light
obstructions above the block. If/when sunlight of a block is updated
and there is no block above it, this value is checked for determining
whether sunlight comes from the top.
* `0x02`: `day_night_differs`: Whether the lighting of the block is different
on day and night. Only blocks that have this bit set are updated when
day transforms to night.
* `0x04`: `lighting_expired`: Not used in version 27 and above. If true,
lighting is invalid and should be updated. If you can't calculate
lighting in your generator properly, you could try setting this 1 to
everything and setting the uppermost block in every sector as
`is_underground=0`. It most likely won't work properly.
* `0x08`: `generated`: True if the block has been generated. If false, block
is mostly filled with `CONTENT_IGNORE` and is likely to contain e.g. parts
of trees of neighboring blocks.
`u16` lighting_complete
* Added in version 27.
* This contains 12 flags, each of them corresponds to a direction.
* Indicates if the light is correct at the sides of a map block.
Lighting may not be correct if the light changed, but a neighbor
block was not loaded at that time.
If these flags are false, Minetest will automatically recompute light
when both this block and its required neighbor are loaded.
* The bit order is:
nothing, nothing, nothing, nothing,
night X-, night Y-, night Z-, night Z+, night Y+, night X+,
day X-, day Y-, day Z-, day Z+, day Y+, day X+.
Where 'day' is for the day light bank, 'night' is for the night
light bank.
The 'nothing' bits should be always set, as they will be used
to indicate if direct sunlight spreading is finished.
* Example: if the block at `(0, 0, 0)` has `lighting_complete = 0b1111111111111110`,
Minetest will correct lighting in the day light bank when the block at
`(1, 0, 0)` is also loaded.
Timestamp and node ID mappings were introduced in map format version 29.
* `u32` timestamp
* Timestamp when last saved, as seconds from starting the game.
* `0xffffffff` = invalid/unknown timestamp, nothing should be done with the time
difference when loaded
* `u8` `name_id_mapping_version`
* Should be zero for map format version 29.
* `u16` `num_name_id_mappings`
* foreach `num_name_id_mappings`:
* `u16` `id`
* `u16` `name_len`
* `u8[name_len]` `name`
`u8` content_width
* Number of bytes in the content (`param0`) fields of nodes
* 1 byte before map format version 24, 2 bytes since
`u8` params_width
* Number of bytes used for parameters per node
* Always 2
## Node Data
> **Note**: Zlib-compressed before map format version 29
* If `content_width` is 1:
* `u8[4096]`: `param0` fields
* `u8[4096]`: `param1` fields
* `u8[4096]`: `param2` fields
* If `content_width` is 2:
* `u16[4096]`: `param0` fields
* `u8[4096]`: `param1` fields
* `u8[4096]`: `param2` fields
* The location of a node in each of those arrays is `(z*16*16 + y*16 + x)`.
### Node Metadata List
> **Note**: Zlib-compressed before map version format 29
* Before map format version 23:
* `u16` version (=1)
* `u16` count of metadata
* foreach count:
* `u16` position (`(p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X)`)
* `u16` type_id
* `u16` content_size
* `u8[content_size]` content of metadata. Format depends on `type_id`, see below.
* Since map format version 23:
* `u8` version
> **Note**: Type was `u16` before map format version 23
* = 1 before map format version 28
* = 2 since map format version 28
* `u16` count of metadata
* foreach count:
* `u16` `position` (`(p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X)`)
* `u32` `num_vars`
* foreach `num_vars`:
* `u16` `key_len`
* `u8[key_len]` `key`
* `u32 val_len`
* `u8[val_len]` `value`
* `u8` `is_private`
* only since map format version 2. 0 = not private, 1 = private
* serialized inventory
## Node Timers
* Map format version 23:
* `u8` unused version (always 0)
* Map format version 24:
> **Note**: Not released as stable
* `u8` `nodetimer_version`
* if `nodetimer_version` == 1:
* `u16` `num_of_timers`
* foreach `num_of_timers`:
* `u16` timer position (`(z*16*16 + y*16 + x)`)
* `s32` timeout * 1000
* `s32` elapsed * 1000
* Since map format version 25:
* `u8` length of the data of a single timer (always 2+4+4=10)
* `u16` `num_of_timers`
* foreach `num_of_timers`:
* `u16` timer position (`(z*16*16 + y*16 + x)`)
* `s32` timeout * 1000
* `s32` elapsed * 1000
`u8` static object version:
* Always 0
`u16` `static_object_count`
foreach `static_object_count`:
* `u8` type (object type-id)
* `s32` `pos_x_nodes` * 10000
* `s32` `pos_y_nodes` * 10000
* `s32` `pos_z_nodes` * 10000
* `u16` `data_size`
* `u8[data_size]` `data`
Before map format version 29:
* `u32` `timestamp`
* Same meaning as the timestamp further up
* `u8` `name-id-mapping` version
* Always 0
* `u16` `num_name_id_mappings`
* foreach `num_name_id_mappings`:
* `u16` `id`
* `u16` `name_len`
* `u8[name_len]` `name`
End of File (EOF).
# Format of Nodes
A node is composed of the `u8` fields `param0`, `param1` and `param2`.
Before map format version 24:
* The content id of a node is determined as so:
* If `param0` < `0x80`, `content_id = param0`
* Otherwise, `content_id = (param0<<4) + (param2>>4)`
Since map format version 24:
* The content id of a node is `param0`.
The purpose of `param1` and `param2` depend on the definition of the node.
# Name-ID-Mapping
The mapping maps node content IDs to node names.
# Node Metadata Format (Before Map Format Version 23)
The node metadata is serialized depending on the `type_id` field.
`1`: Generic metadata
* serialized inventory
* `u32` `len`
* `u8[len]` `text`
* `u16` `len`
* `u8[len]` `owner`
* `u16` `len`
* `u8[len]` `infotext`
* `u16` `len`
* `u8[len]` inventory drawspec
* `u8` `allow_text_input` (bool)
* `u8` `removal_disabled` (bool)
* `u8` `enforce_owner` (bool)
* `u32` `num_vars`
* foreach `num_vars`:
* `u16` `len`
* `u8[len]` `name`
* `u32` `len`
* `u8[len]` `value`
`14`: Sign metadata
* `u16` `text_len`
* `u8[text_len]` `text`
`15`: Chest metadata
* serialized inventory
`16`: Furnace metadata
* To be determined
`17`: Locked Chest metadata
* `u16` `len`
* `u8[len]` `owner`
* serialized inventory
# Static Objects
Static objects are persistent freely moving objects in the world.
Object types:
`1`: Test object
`7`: LuaEntity:
* `u8` `compatibility_byte` (always 1)
* `u16` `len`
* `u8[len]` entity name
* `u32` `len`
* `u8[len]` static data
* `s16` `hp`
* `s32` velocity.x * 10000
* `s32` velocity.y * 10000
* `s32` velocity.z * 10000
* `s32` yaw * 1000
Since protocol version 37:
* `u8` `version2` (=1)
* `s32` pitch * 1000
* `s32` roll * 1000
# Itemstring Format
Examples:
* `'default:dirt 5'`
* `'default:pick_wood 21323'`
* `'"default:apple" 2'`
* `'default:apple'`
Older formats:
* `'node "default:dirt" 5'`
* `'NodeItem default:dirt 5'`
* `'ToolItem WPick 21323'`
The wear value in tools is 0...65535.
# Inventory Serialization Format
* The inventory serialization format is line-based.
* The newline character used is `\n`
* The end condition of a serialized inventory is always `EndInventory\n`
* All the slots in a list must always be serialized.
## Example
List foo 4
Item default:sapling
Item default:sword_stone 1 10647
Item default:dirt 99
Empty
EndInventoryList
List bar 9
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
EndInventoryList
EndInventory

@ -1,598 +0,0 @@
=============================
Minetest World Format 22...29
=============================
This applies to a world format carrying the block serialization version
22...27, used at least in
- 0.4.dev-20120322 ... 0.4.dev-20120606 (22...23)
- 0.4.0 (23)
- 24 was never released as stable and existed for ~2 days
- 27 was added in 0.4.15-dev
- 29 was added in 5.5.0-dev
The block serialization version does not fully specify every aspect of this
format; if compliance with this format is to be checked, it needs to be
done by detecting if the files and data indeed follows it.
Files
======
Everything is contained in a directory, the name of which is freeform, but
often serves as the name of the world.
Currently the authentication and ban data is stored on a per-world basis.
It can be copied over from an old world to a newly created world.
World
|-- auth.txt ----- Authentication data
|-- auth.sqlite -- Authentication data (SQLite alternative)
|-- env_meta.txt - Environment metadata
|-- ipban.txt ---- Banned ips/users
|-- map_meta.txt - Map metadata
|-- map.sqlite --- Map data
|-- players ------ Player directory
| |-- player1 -- Player file
| '-- Foo ------ Player file
`-- world.mt ----- World metadata
auth.txt
---------
Contains authentication data, player per line.
<name>:<password hash>:<privilege1,...>
Legacy format (until 0.4.12) of password hash is <name><password> SHA1'd,
in the base64 encoding.
Format (since 0.4.13) of password hash is #1#<salt>#<verifier>, with the
parts inside <> encoded in the base64 encoding.
<verifier> is an RFC 2945 compatible SRP verifier,
of the given salt, password, and the player's name lowercased,
using the 2048-bit group specified in RFC 5054 and the SHA-256 hash function.
Example lines:
- Player "celeron55", no password, privileges "interact" and "shout":
celeron55::interact,shout
- Player "Foo", password "bar", privilege "shout", with a legacy password hash:
foo:iEPX+SQWIR3p67lj/0zigSWTKHg:shout
- Player "Foo", password "bar", privilege "shout", with a 0.4.13 pw hash:
foo:#1#hPpy4O3IAn1hsNK00A6wNw#Kpu6rj7McsrPCt4euTb5RA5ltF7wdcWGoYMcRngwDi11cZhPuuR9i5Bo7o6A877TgcEwoc//HNrj9EjR/CGjdyTFmNhiermZOADvd8eu32FYK1kf7RMC0rXWxCenYuOQCG4WF9mMGiyTPxC63VAjAMuc1nCZzmy6D9zt0SIKxOmteI75pAEAIee2hx4OkSXRIiU4Zrxo1Xf7QFxkMY4x77vgaPcvfmuzom0y/fU1EdSnZeopGPvzMpFx80ODFx1P34R52nmVl0W8h4GNo0k8ZiWtRCdrJxs8xIg7z5P1h3Th/BJ0lwexpdK8sQZWng8xaO5ElthNuhO8UQx1l6FgEA:shout
- Player "bar", no password, no privileges:
bar::
auth.sqlite
------------
Contains authentification data as an SQLite database. This replaces auth.txt
above when auth_backend is set to "sqlite3" in world.mt .
This database contains two tables "auth" and "user_privileges":
CREATE TABLE `auth` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` VARCHAR(32) UNIQUE,
`password` VARCHAR(512),
`last_login` INTEGER
);
CREATE TABLE `user_privileges` (
`id` INTEGER,
`privilege` VARCHAR(32),
PRIMARY KEY (id, privilege)
CONSTRAINT fk_id FOREIGN KEY (id) REFERENCES auth (id) ON DELETE CASCADE
);
The "name" and "password" fields of the auth table are the same as the auth.txt
fields (with modern password hash). The "last_login" field is the last login
time as a unix time stamp.
The "user_privileges" table contains one entry per privilege and player.
A player with "interact" and "shout" privileges will have two entries, one
with privilege="interact" and the second with privilege="shout".
env_meta.txt
-------------
Simple global environment variables.
Example content (added indentation):
game_time = 73471
time_of_day = 19118
EnvArgsEnd
ipban.txt
----------
Banned IP addresses and usernames.
Example content (added indentation):
123.456.78.9|foo
123.456.78.10|bar
map_meta.txt
-------------
Simple global map variables.
Example content (added indentation):
seed = 7980462765762429666
[end_of_params]
map.sqlite
-----------
Map data.
See Map File Format below.
player1, Foo
-------------
Player data.
Filename can be anything.
See Player File Format below.
world.mt
---------
World metadata.
Example content (added indentation and - explanations):
gameid = mesetint - name of the game
enable_damage = true - whether damage is enabled or not
creative_mode = false - whether creative mode is enabled or not
backend = sqlite3 - which DB backend to use for blocks (sqlite3, dummy, leveldb, redis, postgresql)
player_backend = sqlite3 - which DB backend to use for player data
readonly_backend = sqlite3 - optionally readonly seed DB (DB file _must_ be located in "readonly" subfolder)
auth_backend = files - which DB backend to use for authentication data
mod_storage_backend = sqlite3 - which DB backend to use for mod storage
server_announce = false - whether the server is publicly announced or not
load_mod_<mod> = false - whether <mod> is to be loaded in this world
For load_mod_<mod>, the possible values are:
* `false` - Do not load the mod.
* `true` - Load the mod from wherever it is found (may cause conflicts if the same mod appears also in some other place).
* `mods/modpack/moddir` - Relative path to the mod
* Must be one of the following:
* `mods/`: mods in the user path's mods folder (ex `/home/user/.minetest/mods`)
* `share/`: mods in the share's mods folder (ex: `/usr/share/minetest/mods`)
* `/path/to/env`: you can use absolute paths to mods inside folders specified with the `MINETEST_MOD_PATH` env variable.
* Other locations and absolute paths are not supported
* Note that `moddir` is the directory name, not the mod name specified in mod.conf.
PostgreSQL backend specific settings:
pgsql_connection = host=127.0.0.1 port=5432 user=mt_user password=mt_password dbname=minetest
pgsql_player_connection = (same parameters as above)
pgsql_readonly_connection = (same parameters as above)
pgsql_auth_connection = (same parameters as above)
pgsql_mod_storage_connection = (same parameters as above)
Redis backend specific settings:
redis_address = 127.0.0.1 - Redis server address
redis_hash = foo - Database hash
redis_port = 6379 - (optional) connection port
redis_password = hunter2 - (optional) server password
Player File Format
===================
- Should be pretty self-explanatory.
- Note: position is in nodes * 10
Example content (added indentation):
hp = 11
name = celeron55
pitch = 39.77
position = (-5231.97,15,1961.41)
version = 1
yaw = 101.37
PlayerArgsEnd
List main 32
Item default:torch 13
Item default:pick_steel 1 50112
Item experimental:tnt
Item default:cobble 99
Item default:pick_stone 1 13104
Item default:shovel_steel 1 51838
Item default:dirt 61
Item default:rail 78
Item default:coal_lump 3
Item default:cobble 99
Item default:leaves 22
Item default:gravel 52
Item default:axe_steel 1 2045
Item default:cobble 98
Item default:sand 61
Item default:water_source 94
Item default:glass 2
Item default:mossycobble
Item default:pick_steel 1 64428
Item animalmaterials:bone
Item default:sword_steel
Item default:sapling
Item default:sword_stone 1 10647
Item default:dirt 99
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
EndInventoryList
List craft 9
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
EndInventoryList
List craftpreview 1
Empty
EndInventoryList
List craftresult 1
Empty
EndInventoryList
EndInventory
Map File Format
================
Minetest maps consist of MapBlocks, chunks of 16x16x16 nodes.
In addition to the bulk node data, MapBlocks stored on disk also contain
other things.
History
--------
We need a bit of history in here. Initially Minetest stored maps in a
format called the "sectors" format. It was a directory/file structure like
this:
sectors2/XXX/ZZZ/YYYY
For example, the MapBlock at (0,1,-2) was this file:
sectors2/000/ffd/0001
Eventually Minetest outgrow this directory structure, as filesystems were
struggling under the amount of files and directories.
Large servers seriously needed a new format, and thus the base of the
current format was invented, suggested by celeron55 and implemented by
JacobF.
SQLite3 was slammed in, and blocks files were directly inserted as blobs
in a single table, indexed by integer primary keys, oddly mangled from
coordinates.
Today we know that SQLite3 allows multiple primary keys (which would allow
storing coordinates separately), but the format has been kept unchanged for
that part. So, this is where it has come.
</history>
So here goes
-------------
map.sqlite is an sqlite3 database, containing a single table, called
"blocks". It looks like this:
CREATE TABLE `blocks` (`pos` INT NOT NULL PRIMARY KEY,`data` BLOB);
The key
--------
"pos" is created from the three coordinates of a MapBlock using this
algorithm, defined here in Python:
def getBlockAsInteger(p):
return int64(p[2]*16777216 + p[1]*4096 + p[0])
def int64(u):
while u >= 2**63:
u -= 2**64
while u <= -2**63:
u += 2**64
return u
It can be converted the other way by using this code:
def getIntegerAsBlock(i):
x = unsignedToSigned(i % 4096, 2048)
i = int((i - x) / 4096)
y = unsignedToSigned(i % 4096, 2048)
i = int((i - y) / 4096)
z = unsignedToSigned(i % 4096, 2048)
return x,y,z
def unsignedToSigned(i, max_positive):
if i < max_positive:
return i
else:
return i - 2*max_positive
The blob
---------
The blob is the data that would have otherwise gone into the file.
See below for description.
MapBlock serialization format
==============================
NOTE: Byte order is MSB first (big-endian).
NOTE: Zlib data is in such a format that Python's zlib at least can
directly decompress.
NOTE: Since version 29 zstd is used instead of zlib. In addition the entire
block is first serialized and then compressed (except the version byte).
u8 version
- map format version number, see serialization.h for the latest number
u8 flags
- Flag bitmasks:
- 0x01: is_underground: Should be set to 0 if there will be no light
obstructions above the block. If/when sunlight of a block is updated
and there is no block above it, this value is checked for determining
whether sunlight comes from the top.
- 0x02: day_night_differs: Whether the lighting of the block is different
on day and night. Only blocks that have this bit set are updated when
day transforms to night.
- 0x04: lighting_expired: Not used in version 27 and above. If true,
lighting is invalid and should be updated. If you can't calculate
lighting in your generator properly, you could try setting this 1 to
everything and setting the uppermost block in every sector as
is_underground=0. I am quite sure it doesn't work properly, though.
- 0x08: generated: True if the block has been generated. If false, block
is mostly filled with CONTENT_IGNORE and is likely to contain eg. parts
of trees of neighboring blocks.
u16 lighting_complete
- Added in version 27.
- This contains 12 flags, each of them corresponds to a direction.
- Indicates if the light is correct at the sides of a map block.
Lighting may not be correct if the light changed, but a neighbor
block was not loaded at that time.
If these flags are false, Minetest will automatically recompute light
when both this block and its required neighbor are loaded.
- The bit order is:
nothing, nothing, nothing, nothing,
night X-, night Y-, night Z-, night Z+, night Y+, night X+,
day X-, day Y-, day Z-, day Z+, day Y+, day X+.
Where 'day' is for the day light bank, 'night' is for the night
light bank.
The 'nothing' bits should be always set, as they will be used
to indicate if direct sunlight spreading is finished.
- Example: if the block at (0, 0, 0) has
lighting_complete = 0b1111111111111110,
then Minetest will correct lighting in the day light bank when
the block at (1, 0, 0) is also loaded.
if map format version >= 29:
u32 timestamp
- Timestamp when last saved, as seconds from starting the game.
- 0xffffffff = invalid/unknown timestamp, nothing should be done with the time
difference when loaded
u8 name_id_mapping_version
- Should be zero for map format version 29.
u16 num_name_id_mappings
foreach num_name_id_mappings
u16 id
u16 name_len
u8[name_len] name
if map format version < 29:
-- Nothing right here, timestamp and node id mappings are serialized later
u8 content_width
- Number of bytes in the content (param0) fields of nodes
if map format version <= 23:
- Always 1
if map format version >= 24:
- Always 2
u8 params_width
- Number of bytes used for parameters per node
- Always 2
node data (zlib-compressed if version < 29):
if content_width == 1:
- content:
u8[4096]: param0 fields
u8[4096]: param1 fields
u8[4096]: param2 fields
if content_width == 2:
- content:
u16[4096]: param0 fields
u8[4096]: param1 fields
u8[4096]: param2 fields
- The location of a node in each of those arrays is (z*16*16 + y*16 + x).
node metadata list (zlib-compressed if version < 29):
- content:
if map format version <= 22:
u16 version (=1)
u16 count of metadata
foreach count:
u16 position (p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X)
u16 type_id
u16 content_size
u8[content_size] content of metadata. Format depends on type_id, see below.
if map format version >= 23:
u8 version -- Note: type was u16 for map format version <= 22
-- = 1 for map format version < 28
-- = 2 since map format version 28
u16 count of metadata
foreach count:
u16 position (p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X)
u32 num_vars
foreach num_vars:
u16 key_len
u8[key_len] key
u32 val_len
u8[val_len] value
u8 is_private -- only for version >= 2. 0 = not private, 1 = private
serialized inventory
- Node timers
if map format version == 23:
u8 unused version (always 0)
if map format version == 24: (NOTE: Not released as stable)
u8 nodetimer_version
if nodetimer_version == 0:
(nothing else)
if nodetimer_version == 1:
u16 num_of_timers
foreach num_of_timers:
u16 timer position (z*16*16 + y*16 + x)
s32 timeout*1000
s32 elapsed*1000
if map format version >= 25:
-- Nothing right here, node timers are serialized later
u8 static object version:
- Always 0
u16 static_object_count
foreach static_object_count:
u8 type (object type-id)
s32 pos_x_nodes * 10000
s32 pos_y_nodes * 10000
s32 pos_z_nodes * 10000
u16 data_size
u8[data_size] data
if map format version < 29:
u32 timestamp
- Same meaning as the timestamp further up
u8 name-id-mapping version
- Always 0
u16 num_name_id_mappings
foreach num_name_id_mappings
u16 id
u16 name_len
u8[name_len] name
- Node timers
if map format version >= 25:
u8 length of the data of a single timer (always 2+4+4=10)
u16 num_of_timers
foreach num_of_timers:
u16 timer position (z*16*16 + y*16 + x)
s32 timeout*1000
s32 elapsed*1000
EOF.
Format of nodes
----------------
A node is composed of the u8 fields param0, param1 and param2.
if map format version <= 23:
The content id of a node is determined as so:
- If param0 < 0x80,
content_id = param0
- Otherwise
content_id = (param0<<4) + (param2>>4)
if map format version >= 24:
The content id of a node is param0.
The purpose of param1 and param2 depend on the definition of the node.
The name-id-mapping
--------------------
The mapping maps node content ids to node names.
Node metadata format for map format versions <= 22
---------------------------------------------------
The node metadata are serialized depending on the type_id field.
1: Generic metadata
serialized inventory
u32 len
u8[len] text
u16 len
u8[len] owner
u16 len
u8[len] infotext
u16 len
u8[len] inventory drawspec
u8 allow_text_input (bool)
u8 removal_disabled (bool)
u8 enforce_owner (bool)
u32 num_vars
foreach num_vars
u16 len
u8[len] name
u32 len
u8[len] value
14: Sign metadata
u16 text_len
u8[text_len] text
15: Chest metadata
serialized inventory
16: Furnace metadata
TBD
17: Locked Chest metadata
u16 len
u8[len] owner
serialized inventory
Static objects
---------------
Static objects are persistent freely moving objects in the world.
Object types:
1: Test object
7: LuaEntity
7: LuaEntity:
u8 compatibility_byte (always 1)
u16 len
u8[len] entity name
u32 len
u8[len] static data
s16 hp
s32 velocity.x * 10000
s32 velocity.y * 10000
s32 velocity.z * 10000
s32 yaw * 1000
if PROTOCOL_VERSION >= 37:
u8 version2 (=1)
s32 pitch * 1000
s32 roll * 1000
Itemstring format
------------------
eg. 'default:dirt 5'
eg. 'default:pick_wood 21323'
eg. '"default:apple" 2'
eg. 'default:apple'
- The wear value in tools is 0...65535
- There are also a number of older formats that you might stumble upon:
eg. 'node "default:dirt" 5'
eg. 'NodeItem default:dirt 5'
eg. 'ToolItem WPick 21323'
Inventory serialization format
-------------------------------
- The inventory serialization format is line-based
- The newline character used is "\n"
- The end condition of a serialized inventory is always "EndInventory\n"
- All the slots in a list must always be serialized.
Example (format does not include "---"):
---
List foo 4
Item default:sapling
Item default:sword_stone 1 10647
Item default:dirt 99
Empty
EndInventoryList
List bar 9
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
Empty
EndInventoryList
EndInventory
---

@ -3,7 +3,7 @@ minetest.register_on_joinplayer(function(player)
if not player or not player:is_player() then
return
end
minetest.chat_send_player(player:get_player_name(), "This is the \"Development Test\" [devtest], meant only for testing and development. Use Minetest Game for the real thing.")
minetest.chat_send_player(player:get_player_name(), "This is the \"Development Test\" [devtest], meant only for testing and development.")
end
minetest.after(2.0, cb, player)
end)

@ -428,17 +428,17 @@ local MAX_BOUNCE_NONJUMPY = 140
for i=-MAX_BOUNCE_NONJUMPY, MAX_BOUNCE_JUMPY, 20 do
if i ~= 0 then
local desc
local val = math.floor(((i-20)/200)*255)
local val2 = math.max(0, 200 - val)
local val = math.floor((math.abs(i) - 20) / 200 * 255)
local val2 = math.max(0, 255 - val)
local num = string.format("%03d", math.abs(i))
if i > 0 then
desc = S("Bouncy Node (@1%), jumpy", i).."\n"..
S("Sneaking/jumping affects bounce")
color = { r=255-val, g=255-val, b=255, a=255 }
color = { r=val2, g=val2, b=255, a=255 }
else
desc = S("Bouncy Node (@1%), non-jumpy", math.abs(i)).."\n"..
S("Sneaking/jumping does not affect bounce")
color = { r=val, g=255, b=val, a=255 }
color = { r=val2, g=255, b=val2, a=255 }
num = "NEG"..num
end
minetest.register_node("testnodes:bouncy"..num, {

@ -201,13 +201,13 @@ full_brightness (Full brightness) bool false
# type: bool
# enable_particles = true
### 3d
### 3D
# 3D support.
# Currently supported:
# - none: no 3d output.
# - anaglyph: cyan/magenta color 3d.
# - interlaced: odd/even line based polarisation screen support.
# - interlaced: odd/even line based polarization screen support.
# - topbottom: split screen top/bottom.
# - sidebyside: split screen side by side.
# - crossview: Cross-eyed 3d
@ -319,7 +319,7 @@ full_brightness (Full brightness) bool false
### Clouds
# Clouds are a client side effect.
# Clouds are a client-side effect.
# type: bool
# enable_clouds = true
@ -330,7 +330,7 @@ full_brightness (Full brightness) bool false
### Filtering and Antialiasing
# Use mipmaps when scaling textures down. May slightly increase performance,
# especially when using a high resolution texture pack.
# especially when using a high-resolution texture pack.
# Gamma-correct downscaling is not supported.
# type: bool
# mip_map = false
@ -368,7 +368,7 @@ full_brightness (Full brightness) bool false
# type: enum values: none, fsaa, fxaa, ssaa
# antialiasing = none
# Defines size of the sampling grid for FSAA and SSAA antializasing methods.
# Defines the size of the sampling grid for FSAA and SSAA antialiasing methods.
# Value of 2 means taking 2x2 = 4 samples.
# type: enum values: 2, 4, 8, 16
# fsaa = 2
@ -472,7 +472,7 @@ full_brightness (Full brightness) bool false
# type: bool
# shadow_map_color = false
# Spread a complete update of shadow map over given amount of frames.
# Spread a complete update of shadow map over given number of frames.
# Higher values might make shadows laggy, lower values
# will consume more resources.
# Minimum value: 1; maximum value: 16
@ -565,7 +565,7 @@ full_brightness (Full brightness) bool false
# type: enum values: , be, bg, ca, cs, da, de, el, en, eo, es, et, eu, fi, fr, gd, gl, hu, id, it, ja, jbo, kk, ko, lt, lv, ms, nb, nl, nn, pl, pt, pt_BR, ro, ru, sk, sl, sr_Cyrl, sr_Latn, sv, sw, tr, uk, vi, zh_CN, zh_TW
# language =
### GUIs
### GUI
# Scale GUI by a user specified value.
# Use a nearest-neighbor-anti-alias filter to scale the GUI.
@ -837,7 +837,7 @@ full_brightness (Full brightness) bool false
# type: int min: 10 max: 65535
# chat_message_max_size = 500
# Amount of messages a player may send per 10 seconds.
# Number of messages a player may send per 10 seconds.
# type: float min: 1
# chat_message_limit_per_10sec = 8.0
@ -959,7 +959,7 @@ full_brightness (Full brightness) bool false
# type: flags possible values: caves, dungeons, light, decorations, biomes, ores, nocaves, nodungeons, nolight, nodecorations, nobiomes, noores
# mg_flags = caves,dungeons,light,decorations,biomes,ores
## Biome API noise parameters
## Biome API
# Temperature variation for biomes.
# type: noise_params_2d
@ -1397,7 +1397,7 @@ full_brightness (Full brightness) bool false
# to above 'mgv7_floatland_ymax' - 'mgv7_floatland_taper' (the start of the
# upper tapering).
# ***WARNING, POTENTIAL DANGER TO WORLDS AND SERVER PERFORMANCE***:
# When enabling water placement the floatlands must be configured and tested
# When enabling water placement, floatlands must be configured and tested
# to be a solid layer by setting 'mgv7_floatland_density' to 2.0 (or other
# required value depending on 'mgv7_np_floatland'), to avoid
# server-intensive extreme water flow and to avoid vast flooding of the
@ -2312,7 +2312,7 @@ full_brightness (Full brightness) bool false
# mgvalleys_spflags = altitude_chill,humid_rivers,vary_river_depth,altitude_dry
# The vertical distance over which heat drops by 20 if 'altitude_chill' is
# enabled. Also the vertical distance over which humidity drops by 10 if
# enabled. Also, the vertical distance over which humidity drops by 10 if
# 'altitude_dry' is enabled.
# type: int min: 0 max: 65535
# mgvalleys_altitude_chill = 90
@ -2403,7 +2403,7 @@ full_brightness (Full brightness) bool false
# flags =
# }
# The depth of dirt or other biome filler node.
# Variation of biome filler depth.
# type: noise_params_2d
# mgvalleys_np_filler_depth = {
# offset = 0,
@ -2604,7 +2604,7 @@ full_brightness (Full brightness) bool false
# type: enum values: txt, csv, lua, json, json_pretty
# profiler.default_report_format = txt
# The file path relative to your worldpath in which profiles will be saved to.
# The file path relative to your world path in which profiles will be saved to.
# type: string
# profiler.report_path =
@ -2641,7 +2641,7 @@ full_brightness (Full brightness) bool false
# type: bool
# instrument.profiler = false
### Engine profiler
### Engine Profiler
# Print the engine's profiling data in regular intervals (in seconds).
# 0 = disable. Useful for developers.
@ -2857,7 +2857,7 @@ full_brightness (Full brightness) bool false
# type: string
# prometheus_listener_address = 127.0.0.1:30000
# Maximum size of the out chat queue.
# Maximum size of the outgoing chat queue.
# 0 to disable queueing and -1 to make the queue size unlimited.
# type: int min: -1 max: 32767
# max_out_chat_queue_size = 20
@ -3024,17 +3024,17 @@ full_brightness (Full brightness) bool false
# type: int min: 2 max: 32767
# block_send_optimize_distance = 4
# If enabled the server will perform map block occlusion culling based on
# If enabled, the server will perform map block occlusion culling based on
# on the eye position of the player. This can reduce the number of blocks
# sent to the client 50-80%. The client will not longer receive most invisible
# so that the utility of noclip mode is reduced.
# sent to the client by 50-80%. Clients will no longer receive most
# invisible blocks, so that the utility of noclip mode is reduced.
# type: bool
# server_side_occlusion_culling = true
### Mapgen
# Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes).
# WARNING!: There is no benefit, and there are several dangers, in
# WARNING: There is no benefit, and there are several dangers, in
# increasing this value above 5.
# Reducing this value increases cave and dungeon density.
# Altering this value is for special usage, leaving it unchanged is
@ -3091,7 +3091,7 @@ full_brightness (Full brightness) bool false
# type: int min: 5000 max: 2147483647
# curl_file_download_timeout = 300000
### Misc
### Miscellaneous
# Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.
# type: int min: 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1034,9 +1034,9 @@ void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *
s32 yaw = myplayer->getYaw() * 100;
u32 keyPressed = myplayer->control.getKeysPressed();
// scaled by 80, so that pi can fit into a u8
u8 fov = clientMap->getCameraFov() * 80;
u8 wanted_range = MYMIN(255,
std::ceil(clientMap->getControl().wanted_range / MAP_BLOCKSIZE));
u8 fov = std::fmin(255.0f, clientMap->getCameraFov() * 80.0f);
u8 wanted_range = std::fmin(255.0f,
std::ceil(clientMap->getWantedRange() * (1.0f / MAP_BLOCKSIZE)));
v3s32 position(pf.X, pf.Y, pf.Z);
v3s32 speed(sf.X, sf.Y, sf.Z);
@ -1385,8 +1385,9 @@ void Client::sendPlayerPos()
return;
ClientMap &map = m_env.getClientMap();
u8 camera_fov = map.getCameraFov();
u8 wanted_range = map.getControl().wanted_range;
u8 camera_fov = std::fmin(255.0f, map.getCameraFov() * 80.0f);
u8 wanted_range = std::fmin(255.0f,
std::ceil(map.getWantedRange() * (1.0f / MAP_BLOCKSIZE)));
u32 keyPressed = player->control.getKeysPressed();
bool camera_inverted = m_camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT;

@ -819,6 +819,7 @@ protected:
const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
const CameraOrientation &cam);
void updateClouds(float dtime);
void updateShadows();
// Misc
@ -4012,33 +4013,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
/*
Update clouds
*/
if (clouds) {
if (sky->getCloudsVisible()) {
clouds->setVisible(true);
clouds->step(dtime);
// camera->getPosition is not enough for 3rd person views
v3f camera_node_position = camera->getCameraNode()->getPosition();
v3s16 camera_offset = camera->getOffset();
camera_node_position.X = camera_node_position.X + camera_offset.X * BS;
camera_node_position.Y = camera_node_position.Y + camera_offset.Y * BS;
camera_node_position.Z = camera_node_position.Z + camera_offset.Z * BS;
clouds->update(camera_node_position,
sky->getCloudColor());
if (clouds->isCameraInsideCloud() && m_cache_enable_fog) {
// if inside clouds, and fog enabled, use that as sky
// color(s)
video::SColor clouds_dark = clouds->getColor()
.getInterpolated(video::SColor(255, 0, 0, 0), 0.9);
sky->overrideColors(clouds_dark, clouds->getColor());
sky->setInClouds(true);
runData.fog_range = std::fmin(runData.fog_range * 0.5f, 32.0f * BS);
// do not draw clouds after all
clouds->setVisible(false);
}
} else {
clouds->setVisible(false);
}
}
if (clouds)
updateClouds(dtime);
/*
Update particles
@ -4217,6 +4193,33 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
g_profiler->avg("Game::updateFrame(): update frame [ms]", tt_update.stop(true));
}
void Game::updateClouds(float dtime)
{
if (this->sky->getCloudsVisible()) {
this->clouds->setVisible(true);
this->clouds->step(dtime);
// this->camera->getPosition is not enough for third-person camera.
v3f camera_node_position = this->camera->getCameraNode()->getPosition();
v3s16 camera_offset = this->camera->getOffset();
camera_node_position.X = camera_node_position.X + camera_offset.X * BS;
camera_node_position.Y = camera_node_position.Y + camera_offset.Y * BS;
camera_node_position.Z = camera_node_position.Z + camera_offset.Z * BS;
this->clouds->update(camera_node_position, this->sky->getCloudColor());
if (this->clouds->isCameraInsideCloud() && this->m_cache_enable_fog) {
// If camera is inside cloud and fog is enabled, use cloud's colors as sky colors.
video::SColor clouds_dark = this->clouds->getColor().getInterpolated(
video::SColor(255, 0, 0, 0), 0.9);
this->sky->overrideColors(clouds_dark, this->clouds->getColor());
this->sky->setInClouds(true);
this->runData.fog_range = std::fmin(this->runData.fog_range * 0.5f, 32.0f * BS);
// Clouds are not drawn in this case.
this->clouds->setVisible(false);
}
} else {
this->clouds->setVisible(false);
}
}
/* Log times and stuff for visualization */
inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
{

@ -169,7 +169,6 @@ private:
LocalPlayer *m_player;
ParticleSpawnerParameters p;
std::vector<ClientParticleTexture> m_texpool;
size_t m_texcount;
std::vector<float> m_spawntimes;
u16 m_attached_id;
};

@ -266,8 +266,8 @@ void GUIScrollBar::setPos(const s32 &pos)
}
if (is_auto_scaling)
thumb_size = s32(thumb_area /
(f32(page_size) / f32(thumb_area + border_size * 2)));
thumb_size = (s32)std::fmin(S32_MAX,
thumb_area / (f32(page_size) / f32(thumb_area + border_size * 2)));
thumb_size = core::s32_clamp(thumb_size, thumb_min, thumb_area);
scroll_pos = core::s32_clamp(pos, min_pos, max_pos);

@ -70,15 +70,15 @@ void GUIVolumeChange::regenerateGui(v2u32 screensize)
Add stuff
*/
{
core::rect<s32> rect(0, 0, 160 * s, 20 * s);
rect = rect + v2s32(size.X / 2 - 80 * s, size.Y / 2 - 70 * s);
core::rect<s32> rect(0, 0, 300 * s, 20 * s);
rect = rect + v2s32(size.X / 2 - 150 * s, size.Y / 2 - 70 * s);
StaticText::add(Environment, fwgettext("Sound Volume: %d%%", volume),
rect, false, true, this, ID_soundText);
}
{
core::rect<s32> rect(0, 0, 80 * s, 30 * s);
rect = rect + v2s32(size.X / 2 - 80 * s / 2, size.Y / 2 + 55 * s);
core::rect<s32> rect(0, 0, 100 * s, 30 * s);
rect = rect + v2s32(size.X / 2 - 100 * s / 2, size.Y / 2 + 55 * s);
GUIButton::addButton(Environment, rect, m_tsrc, this, ID_soundExitButton,
wstrgettext("Exit").c_str());
}
@ -91,8 +91,8 @@ void GUIVolumeChange::regenerateGui(v2u32 screensize)
e->setPos(volume);
}
{
core::rect<s32> rect(0, 0, 160 * s, 20 * s);
rect = rect + v2s32(size.X / 2 - 80 * s, size.Y / 2 - 35 * s);
core::rect<s32> rect(0, 0, 300 * s, 20 * s);
rect = rect + v2s32(size.X / 2 - 150 * s, size.Y / 2 - 35 * s);
Environment->addCheckBox(g_settings->getBool("mute_sound"), rect, this,
ID_soundMuteButton, wstrgettext("Muted").c_str());
}

@ -78,6 +78,7 @@ extern "C" {
#define DEBUGFILE "debug.txt"
#define DEFAULT_SERVER_PORT 30000
#define ENV_MT_LOGCOLOR "MT_LOGCOLOR"
#define ENV_NO_COLOR "NO_COLOR"
#define ENV_CLICOLOR "CLICOLOR"
#define ENV_CLICOLOR_FORCE "CLICOLOR_FORCE"
@ -287,6 +288,13 @@ int main(int argc, char *argv[])
static void get_env_opts(Settings &args)
{
#if !defined(_WIN32)
const char *mt_logcolor = std::getenv(ENV_MT_LOGCOLOR);
if (mt_logcolor) {
args.set("color", mt_logcolor);
}
#endif
// CLICOLOR is a de-facto standard option for colors <https://bixense.com/clicolors/>
// CLICOLOR != 0: ANSI colors are supported (auto-detection, this is the default)
// CLICOLOR == 0: ANSI colors are NOT supported
@ -493,12 +501,6 @@ static bool setup_log_params(const Settings &cmd_args)
std::string color_mode;
if (cmd_args.exists("color")) {
color_mode = cmd_args.get("color");
#if !defined(_WIN32)
} else {
char *color_mode_env = getenv("MT_LOGCOLOR");
if (color_mode_env)
color_mode = color_mode_env;
#endif
}
if (!color_mode.empty()) {
if (color_mode == "auto") {
@ -550,8 +552,7 @@ static bool create_userdata_path()
}
namespace {
std::string findProgram(const char *name)
{
[[maybe_unused]] std::string findProgram(const char *name) {
char *path_c = getenv("PATH");
if (!path_c)
return "";
@ -571,8 +572,9 @@ namespace {
#ifdef _WIN32
const char *debuggerNames[] = {"gdb.exe", "lldb.exe"};
#else
const char *debuggerNames[] = {"gdb", "lldb"};
[[maybe_unused]] const char *debuggerNames[] = {"gdb", "lldb"};
#endif
template <class T>
void getDebuggerArgs(T &out, int i) {
if (i == 0) {

@ -294,19 +294,23 @@ bool read_color(lua_State *L, int index, video::SColor *color)
video::SColor read_ARGB8(lua_State *L, int index)
{
auto clamp_col = [](double c) -> u32 {
return std::fmax(0.0, std::fmin(255.0, c));
};
video::SColor color(0);
CHECK_TYPE(index, "ARGB color", LUA_TTABLE);
lua_getfield(L, index, "a");
color.setAlpha(lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0xFF);
color.setAlpha(lua_isnumber(L, -1) ? clamp_col(lua_tonumber(L, -1)) : 0xFF);
lua_pop(L, 1);
lua_getfield(L, index, "r");
color.setRed(lua_tonumber(L, -1));
color.setRed(clamp_col(lua_tonumber(L, -1)));
lua_pop(L, 1);
lua_getfield(L, index, "g");
color.setGreen(lua_tonumber(L, -1));
color.setGreen(clamp_col(lua_tonumber(L, -1)));
lua_pop(L, 1);
lua_getfield(L, index, "b");
color.setBlue(lua_tonumber(L, -1));
color.setBlue(clamp_col(lua_tonumber(L, -1)));
lua_pop(L, 1);
return color;
}

@ -82,9 +82,9 @@ fake_function() {
gettext("Enables tradeoffs that reduce CPU load or increase rendering performance\nat the expense of minor visual glitches that do not impact game playability.");
gettext("Digging particles");
gettext("Adds particles when digging a node.");
gettext("3d");
gettext("3D");
gettext("3D mode");
gettext("3D support.\nCurrently supported:\n- none: no 3d output.\n- anaglyph: cyan/magenta color 3d.\n- interlaced: odd/even line based polarisation screen support.\n- topbottom: split screen top/bottom.\n- sidebyside: split screen side by side.\n- crossview: Cross-eyed 3d\nNote that the interlaced mode requires shaders to be enabled.");
gettext("3D support.\nCurrently supported:\n- none: no 3d output.\n- anaglyph: cyan/magenta color 3d.\n- interlaced: odd/even line based polarization screen support.\n- topbottom: split screen top/bottom.\n- sidebyside: split screen side by side.\n- crossview: Cross-eyed 3d\nNote that the interlaced mode requires shaders to be enabled.");
gettext("3D mode parallax strength");
gettext("Strength of 3D mode parallax.");
gettext("Bobbing");
@ -130,12 +130,12 @@ fake_function() {
gettext("Fraction of the visible distance at which fog starts to be rendered");
gettext("Clouds");
gettext("Clouds");
gettext("Clouds are a client side effect.");
gettext("Clouds are a client-side effect.");
gettext("3D clouds");
gettext("Use 3D cloud look instead of flat.");
gettext("Filtering and Antialiasing");
gettext("Mipmapping");
gettext("Use mipmaps when scaling textures down. May slightly increase performance,\nespecially when using a high resolution texture pack.\nGamma-correct downscaling is not supported.");
gettext("Use mipmaps when scaling textures down. May slightly increase performance,\nespecially when using a high-resolution texture pack.\nGamma-correct downscaling is not supported.");
gettext("Bilinear filtering");
gettext("Use bilinear filtering when scaling textures down.");
gettext("Trilinear filtering");
@ -145,7 +145,7 @@ fake_function() {
gettext("Antialiasing method");
gettext("Select the antialiasing method to apply.\n\n* None - No antialiasing (default)\n\n* FSAA - Hardware-provided full-screen antialiasing (incompatible with shaders)\nA.K.A multi-sample antialiasing (MSAA)\nSmoothens out block edges but does not affect the insides of textures.\nA restart is required to change this option.\n\n* FXAA - Fast approximate antialiasing (requires shaders)\nApplies a post-processing filter to detect and smoothen high-contrast edges.\nProvides balance between speed and image quality.\n\n* SSAA - Super-sampling antialiasing (requires shaders)\nRenders higher-resolution image of the scene, then scales down to reduce\nthe aliasing effects. This is the slowest and the most accurate method.");
gettext("Anti-aliasing scale");
gettext("Defines size of the sampling grid for FSAA and SSAA antializasing methods.\nValue of 2 means taking 2x2 = 4 samples.");
gettext("Defines the size of the sampling grid for FSAA and SSAA antialiasing methods.\nValue of 2 means taking 2x2 = 4 samples.");
gettext("Occlusion Culling");
gettext("Occlusion Culler");
gettext("Type of occlusion_culler\n\n\"loops\" is the legacy algorithm with nested loops and O(n³) complexity\n\"bfs\" is the new algorithm based on breadth-first-search and side culling\n\nThis setting should only be changed if you have performance problems.");
@ -185,7 +185,7 @@ fake_function() {
gettext("Colored shadows");
gettext("Enable colored shadows.\nOn true translucent nodes cast colored shadows. This is expensive.");
gettext("Map shadows update frames");
gettext("Spread a complete update of shadow map over given amount of frames.\nHigher values might make shadows laggy, lower values\nwill consume more resources.\nMinimum value: 1; maximum value: 16");
gettext("Spread a complete update of shadow map over given number of frames.\nHigher values might make shadows laggy, lower values\nwill consume more resources.\nMinimum value: 1; maximum value: 16");
gettext("Soft shadow radius");
gettext("Set the soft shadow radius size.\nLower values mean sharper shadows, bigger values mean softer shadows.\nMinimum value: 1.0; maximum value: 15.0");
gettext("Sky Body Orbit Tilt");
@ -216,7 +216,7 @@ fake_function() {
gettext("User Interfaces");
gettext("Language");
gettext("Set the language. Leave empty to use the system language.\nA restart is required after changing this.");
gettext("GUIs");
gettext("GUI");
gettext("GUI scaling");
gettext("Scale GUI by a user specified value.\nUse a nearest-neighbor-anti-alias filter to scale the GUI.\nThis will smooth over some of the rough edges, and blend\npixels when scaling down, at the cost of blurring some\nedge pixels when images are scaled by non-integer sizes.");
gettext("Inventory items animations");
@ -294,7 +294,7 @@ fake_function() {
gettext("Message of the day displayed to players connecting.");
gettext("Maximum users");
gettext("Maximum number of players that can be connected simultaneously.");
gettext("Static spawnpoint");
gettext("Static spawn point");
gettext("If this is set, players will always (re)spawn at the given position.");
gettext("Networking");
gettext("Server port");
@ -323,7 +323,7 @@ fake_function() {
gettext("Client-side Modding");
gettext("Client side modding restrictions");
gettext("Restricts the access of certain client-side functions on servers.\nCombine the byteflags below to restrict client-side features, or set to 0\nfor no restrictions:\nLOAD_CLIENT_MODS: 1 (disable loading client-provided mods)\nCHAT_MESSAGES: 2 (disable send_chat_message call client-side)\nREAD_ITEMDEFS: 4 (disable get_item_def call client-side)\nREAD_NODEDEFS: 8 (disable get_node_def call client-side)\nLOOKUP_NODES_LIMIT: 16 (limits get_node call client-side to\ncsm_restriction_noderange)\nREAD_PLAYERINFO: 32 (disable get_player_names call client-side)");
gettext("Client side node lookup range restriction");
gettext("Client-side node lookup range restriction");
gettext("If the CSM restriction for node range is enabled, get_node calls are limited\nto this distance from the player to the node.");
gettext("Chat");
gettext("Strip color codes");
@ -331,7 +331,7 @@ fake_function() {
gettext("Chat message max length");
gettext("Set the maximum length of a chat message (in characters) sent by clients.");
gettext("Chat message count limit");
gettext("Amount of messages a player may send per 10 seconds.");
gettext("Number of messages a player may send per 10 seconds.");
gettext("Chat message kick threshold");
gettext("Kick players who sent more than X messages per 10 seconds.");
gettext("Server Gameplay");
@ -381,7 +381,7 @@ fake_function() {
gettext("Limit of map generation, in nodes, in all 6 directions from (0, 0, 0).\nOnly mapchunks completely within the mapgen limit are generated.\nValue is stored per-world.");
gettext("Mapgen flags");
gettext("Global map generation attributes.\nIn Mapgen v6 the 'decorations' flag controls all decorations except trees\nand jungle grass, in all other mapgens this flag controls all decorations.");
gettext("Biome API noise parameters");
gettext("Biome API");
gettext("Heat noise");
gettext("Temperature variation for biomes.");
gettext("Heat blend noise");
@ -484,7 +484,7 @@ fake_function() {
gettext("Floatland density");
gettext("Adjusts the density of the floatland layer.\nIncrease value to increase density. Can be positive or negative.\nValue = 0.0: 50% of volume is floatland.\nValue = 2.0 (can be higher depending on 'mgv7_np_floatland', always test\nto be sure) creates a solid floatland layer.");
gettext("Floatland water level");
gettext("Surface level of optional water placed on a solid floatland layer.\nWater is disabled by default and will only be placed if this value is set\nto above 'mgv7_floatland_ymax' - 'mgv7_floatland_taper' (the start of the\nupper tapering).\n***WARNING, POTENTIAL DANGER TO WORLDS AND SERVER PERFORMANCE***:\nWhen enabling water placement the floatlands must be configured and tested\nto be a solid layer by setting 'mgv7_floatland_density' to 2.0 (or other\nrequired value depending on 'mgv7_np_floatland'), to avoid\nserver-intensive extreme water flow and to avoid vast flooding of the\nworld surface below.");
gettext("Surface level of optional water placed on a solid floatland layer.\nWater is disabled by default and will only be placed if this value is set\nto above 'mgv7_floatland_ymax' - 'mgv7_floatland_taper' (the start of the\nupper tapering).\n***WARNING, POTENTIAL DANGER TO WORLDS AND SERVER PERFORMANCE***:\nWhen enabling water placement, floatlands must be configured and tested\nto be a solid layer by setting 'mgv7_floatland_density' to 2.0 (or other\nrequired value depending on 'mgv7_np_floatland'), to avoid\nserver-intensive extreme water flow and to avoid vast flooding of the\nworld surface below.");
gettext("Cave width");
gettext("Controls width of tunnels, a smaller value creates wider tunnels.\nValue >= 10.0 completely disables generation of tunnels and avoids the\nintensive noise calculations.");
gettext("Large cave depth");
@ -712,7 +712,7 @@ fake_function() {
gettext("Mapgen Valleys specific flags");
gettext("Map generation attributes specific to Mapgen Valleys.\n'altitude_chill': Reduces heat with altitude.\n'humid_rivers': Increases humidity around rivers.\n'vary_river_depth': If enabled, low humidity and high heat causes rivers\nto become shallower and occasionally dry.\n'altitude_dry': Reduces humidity with altitude.");
gettext("Altitude chill");
gettext("The vertical distance over which heat drops by 20 if 'altitude_chill' is\nenabled. Also the vertical distance over which humidity drops by 10 if\n'altitude_dry' is enabled.");
gettext("The vertical distance over which heat drops by 20 if 'altitude_chill' is\nenabled. Also, the vertical distance over which humidity drops by 10 if\n'altitude_dry' is enabled.");
gettext("Large cave depth");
gettext("Depth below which you'll find large caves.");
gettext("Small cave minimum number");
@ -747,7 +747,7 @@ fake_function() {
gettext("Cave noise #2");
gettext("Second of two 3D noises that together define tunnels.");
gettext("Filler depth");
gettext("The depth of dirt or other biome filler node.");
gettext("Variation of biome filler depth.");
gettext("Cavern noise");
gettext("3D noise defining giant caverns.");
gettext("River noise");
@ -796,7 +796,7 @@ fake_function() {
gettext("Default report format");
gettext("The default format in which profiles are being saved,\nwhen calling `/profiler save [format]` without format.");
gettext("Report path");
gettext("The file path relative to your worldpath in which profiles will be saved to.");
gettext("The file path relative to your world path in which profiles will be saved to.");
gettext("Entity methods");
gettext("Instrument the methods of entities on registration.");
gettext("Active Block Modifiers");
@ -811,7 +811,7 @@ fake_function() {
gettext("Instrument builtin.\nThis is usually only needed by core/builtin contributors");
gettext("Profiler");
gettext("Have the profiler instrument itself:\n* Instrument an empty function.\nThis estimates the overhead, that instrumentation is adding (+1 function call).\n* Instrument the sampler being used to update the statistics.");
gettext("Engine profiler");
gettext("Engine Profiler");
gettext("Engine profiling data print interval");
gettext("Print the engine's profiling data in regular intervals (in seconds).\n0 = disable. Useful for developers.");
gettext("Advanced");
@ -891,8 +891,8 @@ fake_function() {
gettext("Networking");
gettext("Prometheus listener address");
gettext("Prometheus listener address.\nIf Minetest is compiled with ENABLE_PROMETHEUS option enabled,\nenable metrics listener for Prometheus on that address.\nMetrics can be fetched on http://127.0.0.1:30000/metrics");
gettext("Maximum size of the out chat queue");
gettext("Maximum size of the out chat queue.\n0 to disable queueing and -1 to make the queue size unlimited.");
gettext("Maximum size of the outgoing chat queue");
gettext("Maximum size of the outgoing chat queue.\n0 to disable queueing and -1 to make the queue size unlimited.");
gettext("Mapblock unload timeout");
gettext("Timeout for client to remove unused map data from memory, in seconds.");
gettext("Mapblock limit");
@ -957,11 +957,11 @@ fake_function() {
gettext("Liquid update interval in seconds.");
gettext("Block send optimize distance");
gettext("At this distance the server will aggressively optimize which blocks are sent to\nclients.\nSmall values potentially improve performance a lot, at the expense of visible\nrendering glitches (some blocks will not be rendered under water and in caves,\nas well as sometimes on land).\nSetting this to a value greater than max_block_send_distance disables this\noptimization.\nStated in mapblocks (16 nodes).");
gettext("Server side occlusion culling");
gettext("If enabled the server will perform map block occlusion culling based on\non the eye position of the player. This can reduce the number of blocks\nsent to the client 50-80%. The client will not longer receive most invisible\nso that the utility of noclip mode is reduced.");
gettext("Server-side occlusion culling");
gettext("If enabled, the server will perform map block occlusion culling based on\non the eye position of the player. This can reduce the number of blocks\nsent to the client by 50-80%. Clients will no longer receive most\ninvisible blocks, so that the utility of noclip mode is reduced.");
gettext("Mapgen");
gettext("Chunk size");
gettext("Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes).\nWARNING!: There is no benefit, and there are several dangers, in\nincreasing this value above 5.\nReducing this value increases cave and dungeon density.\nAltering this value is for special usage, leaving it unchanged is\nrecommended.");
gettext("Size of mapchunks generated by mapgen, stated in mapblocks (16 nodes).\nWARNING: There is no benefit, and there are several dangers, in\nincreasing this value above 5.\nReducing this value increases cave and dungeon density.\nAltering this value is for special usage, leaving it unchanged is\nrecommended.");
gettext("Mapgen debug");
gettext("Dump the mapgen debug information.");
gettext("Absolute limit of queued blocks to emerge");
@ -979,7 +979,7 @@ fake_function() {
gettext("Limits number of parallel HTTP requests. Affects:\n- Media fetch if server uses remote_media setting.\n- Serverlist download and server announcement.\n- Downloads performed by main menu (e.g. mod manager).\nOnly has an effect if compiled with cURL.");
gettext("cURL file download timeout");
gettext("Maximum time a file download (e.g. a mod download) may take, stated in milliseconds.");
gettext("Misc");
gettext("Miscellaneous");
gettext("DPI");
gettext("Adjust dpi configuration to your screen (non X11/Android only) e.g. for 4k screens.");
gettext("Display Density Scaling Factor");
@ -1011,45 +1011,4 @@ fake_function() {
gettext("The dead zone of the joystick");
gettext("Joystick frustum sensitivity");
gettext("The sensitivity of the joystick axes for moving the\nin-game view frustum around.");
gettext("Hide: Temporary Settings");
gettext("Texture path");
gettext("Path to texture directory. All textures are first searched from here.");
gettext("Minimap");
gettext("Enables minimap.");
gettext("Round minimap");
gettext("Shape of the minimap. Enabled = round, disabled = square.");
gettext("Server address");
gettext("Address to connect to.\nLeave this blank to start a local server.\nNote that the address field in the main menu overrides this setting.");
gettext("Remote port");
gettext("Port to connect to (UDP).\nNote that the port field in the main menu overrides this setting.");
gettext("Damage");
gettext("Enable players getting damage and dying.");
gettext("Creative");
gettext("Enable creative mode for all players");
gettext("Player versus player");
gettext("Whether to allow players to damage and kill each other.");
gettext("Flying");
gettext("Player is able to fly without being affected by gravity.\nThis requires the \"fly\" privilege on the server.");
gettext("Pitch move mode");
gettext("If enabled, makes move directions relative to the player's pitch when flying or swimming.");
gettext("Fast movement");
gettext("Fast movement (via the \"Aux1\" key).\nThis requires the \"fast\" privilege on the server.");
gettext("Noclip");
gettext("If enabled together with fly mode, player is able to fly through solid nodes.\nThis requires the \"noclip\" privilege on the server.");
gettext("Continuous forward");
gettext("Continuous forward movement, toggled by autoforward key.\nPress the autoforward key again or the backwards movement to disable.");
gettext("Cinematic mode");
gettext("This can be bound to a key to toggle camera smoothing when looking around.\nUseful for recording videos");
gettext("Show technical names");
gettext("Affects mods and texture packs in the Content and Select Mods menus, as well as\nsetting names.\nControlled by a checkbox in the settings menu.");
gettext("Show advanced settings");
gettext("Controlled by a checkbox in the settings menu.");
gettext("Sound");
gettext("Enables the sound system.\nIf disabled, this completely disables all sounds everywhere and the in-game\nsound controls will be non-functional.\nChanging this setting requires a restart.");
gettext("Last update check");
gettext("Unix timestamp (integer) of when the client last checked for an update\nSet this value to \"disabled\" to never check for updates.");
gettext("Last known version update");
gettext("Version number which was last seen during an update check.\n\nRepresentation: MMMIIIPPP, where M=Major, I=Minor, P=Patch\nEx: 5.5.0 is 005005000");
gettext("Don't show \"reinstall Minetest Game\" notification");
gettext("If this is set to true, the user will never (again) be shown the\n\"reinstall Minetest Game\" notification.");
}

Some files were not shown because too many files have changed in this diff Show More