aboutsummaryrefslogtreecommitdiff
path: root/steles/init.lua
Commit message (Collapse)AuthorAge
* Release 2017-08-26Pierre-Yves Rollo2017-08-26
|
* add intllib support (i18n)fat1152017-08-05
| | | | | | | | -> mods ontime_clocks, signs, signs_roads & steles add french translations add updatepo.sh script to update po/pot files add specific array for full description of steles
* Relicensing to LGPL, fixes and evolutionsPierre-Yves Rollo2016-08-25
|
* Creation of steles modPierre-Yves Rollo2015-11-28
0501b4'>.github/workflows/android.yml42
-rw-r--r--.github/workflows/build.yml31
-rw-r--r--.gitlab-ci.yml193
-rw-r--r--.luacheckrc8
-rw-r--r--.mailmap74
-rw-r--r--AppImageBuilder.yml51
-rw-r--r--CMakeLists.txt29
-rw-r--r--Dockerfile4
-rw-r--r--LICENSE.txt13
-rw-r--r--README.md13
-rw-r--r--build/android/app/build.gradle16
-rw-r--r--build/android/app/src/main/AndroidManifest.xml7
-rw-r--r--build/android/app/src/main/java/net/minetest/minetest/CustomEditText.java45
-rw-r--r--build/android/app/src/main/java/net/minetest/minetest/GameActivity.java77
-rw-r--r--build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java98
-rw-r--r--build/android/app/src/main/res/values/styles.xml9
-rw-r--r--build/android/build.gradle11
-rw-r--r--build/android/gradle/wrapper/gradle-wrapper.properties8
-rw-r--r--build/android/native/build.gradle65
-rw-r--r--builtin/client/chatcommands.lua5
-rw-r--r--builtin/client/register.lua8
-rw-r--r--builtin/common/after.lua6
-rw-r--r--builtin/common/information_formspecs.lua2
-rw-r--r--builtin/common/misc_helpers.lua4
-rw-r--r--builtin/common/tests/vector_spec.lua4
-rw-r--r--builtin/common/vector.lua6
-rw-r--r--builtin/fstk/ui.lua17
-rw-r--r--builtin/game/chat.lua22
-rw-r--r--builtin/game/deprecated.lua25
-rw-r--r--builtin/game/falling.lua52
-rw-r--r--builtin/game/features.lua2
-rw-r--r--builtin/game/item.lua16
-rw-r--r--builtin/game/item_entity.lua5
-rw-r--r--builtin/game/knockback.lua2
-rw-r--r--builtin/game/misc.lua29
-rw-r--r--builtin/game/register.lua9
-rw-r--r--builtin/game/statbars.lua4
-rw-r--r--builtin/init.lua15
-rw-r--r--builtin/mainmenu/common.lua91
-rw-r--r--builtin/mainmenu/dlg_config_world.lua2
-rw-r--r--builtin/mainmenu/dlg_contentstore.lua576
-rw-r--r--builtin/mainmenu/dlg_create_world.lua19
-rw-r--r--builtin/mainmenu/init.lua112
-rw-r--r--builtin/mainmenu/pkgmgr.lua65
-rw-r--r--builtin/mainmenu/serverlistmgr.lua250
-rw-r--r--builtin/mainmenu/tab_credits.lua74
-rw-r--r--builtin/mainmenu/tab_local.lua34
-rw-r--r--builtin/mainmenu/tab_online.lua124
-rw-r--r--builtin/mainmenu/tab_settings.lua116
-rw-r--r--builtin/mainmenu/tab_simple_main.lua220
-rw-r--r--builtin/mainmenu/tests/favorites_wellformed.txt29
-rw-r--r--builtin/mainmenu/tests/serverlistmgr_spec.lua36
-rw-r--r--builtin/profiler/instrumentation.lua1
-rw-r--r--builtin/settingtypes.txt152
-rw-r--r--client/shaders/3d_interlaced_merge/opengl_fragment.glsl4
-rw-r--r--client/shaders/3d_interlaced_merge/opengl_vertex.glsl7
-rw-r--r--client/shaders/default_shader/opengl_fragment.glsl4
-rw-r--r--client/shaders/default_shader/opengl_vertex.glsl8
-rw-r--r--client/shaders/minimap_shader/opengl_fragment.glsl7
-rw-r--r--client/shaders/minimap_shader/opengl_vertex.glsl10
-rw-r--r--client/shaders/nodes_shader/opengl_fragment.glsl169
-rw-r--r--client/shaders/nodes_shader/opengl_vertex.glsl94
-rw-r--r--client/shaders/object_shader/opengl_fragment.glsl110
-rw-r--r--client/shaders/object_shader/opengl_vertex.glsl34
-rw-r--r--client/shaders/selection_shader/opengl_fragment.glsl7
-rw-r--r--client/shaders/selection_shader/opengl_vertex.glsl9
-rw-r--r--client/shaders/stars_shader/opengl_fragment.glsl6
-rw-r--r--client/shaders/stars_shader/opengl_vertex.glsl4
-rw-r--r--clientmods/preview/init.lua4
-rw-r--r--doc/builtin_entities.txt101
-rw-r--r--doc/client_lua_api.txt27
-rw-r--r--doc/lua_api.txt702
-rw-r--r--doc/menu_lua_api.txt38
-rw-r--r--doc/texture_packs.txt26
-rw-r--r--doc/world_format.txt11
-rw-r--r--fonts/DroidSansFallbackFull-LICENSE.txt2
-rw-r--r--fonts/DroidSansFallbackFull.ttfbin4529044 -> 5398328 bytes-rw-r--r--games/devtest/README.md3
-rw-r--r--games/devtest/mods/basenodes/init.lua63
-rw-r--r--games/devtest/mods/basenodes/textures/default_dirt.pngbin790 -> 7303 bytes-rw-r--r--games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass.pngbin0 -> 829 bytes-rw-r--r--games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass_side.png (renamed from games/devtest/mods/basenodes/textures/default_grass_side.png)bin796 -> 796 bytes-rw-r--r--games/devtest/mods/basenodes/textures/info.txt7
-rw-r--r--games/devtest/mods/basetools/init.lua132
-rw-r--r--games/devtest/mods/bucket/init.lua3
-rw-r--r--games/devtest/mods/chest/init.lua19
-rw-r--r--games/devtest/mods/chest_of_everything/init.lua3
-rw-r--r--games/devtest/mods/experimental/commands.lua3
-rw-r--r--games/devtest/mods/experimental/items.lua6
-rw-r--r--games/devtest/mods/soundstuff/init.lua18
-rw-r--r--games/devtest/mods/testentities/armor.lua2
-rw-r--r--games/devtest/mods/testentities/callbacks.lua3
-rw-r--r--games/devtest/mods/testentities/visuals.lua57
-rw-r--r--games/devtest/mods/testfood/init.lua7
-rw-r--r--games/devtest/mods/testfood/textures/testfood_replace.pngbin0 -> 135 bytes-rw-r--r--games/devtest/mods/testformspec/LICENSE.txt14
-rw-r--r--games/devtest/mods/testformspec/formspec.lua42
-rw-r--r--games/devtest/mods/testformspec/models/testformspec_character.b3dbin0 -> 73433 bytes-rw-r--r--games/devtest/mods/testformspec/models/testformspec_chest.obj79
-rw-r--r--games/devtest/mods/testformspec/textures/default_chest_front.pngbin0 -> 423 bytes-rw-r--r--games/devtest/mods/testformspec/textures/default_chest_inside.pngbin0 -> 102 bytes-rw-r--r--games/devtest/mods/testformspec/textures/default_chest_side.pngbin0 -> 375 bytes-rw-r--r--games/devtest/mods/testformspec/textures/default_chest_top.pngbin0 -> 423 bytes-rw-r--r--games/devtest/mods/testformspec/textures/testformspec_character.pngbin0 -> 2754 bytes-rw-r--r--games/devtest/mods/testnodes/drawtypes.lua141
-rw-r--r--games/devtest/mods/testnodes/light.lua6
-rw-r--r--games/devtest/mods/testnodes/liquids.lua16
-rw-r--r--games/devtest/mods/testnodes/mod.conf1
-rw-r--r--games/devtest/mods/testnodes/nodeboxes.lua9
-rw-r--r--games/devtest/mods/testnodes/properties.lua6
-rw-r--r--games/devtest/mods/testnodes/textures.lua18
-rw-r--r--games/devtest/mods/testpathfinder/init.lua6
-rw-r--r--games/devtest/mods/testtools/init.lua76
-rw-r--r--games/devtest/mods/testtools/light.lua22
-rw-r--r--games/devtest/mods/testtools/textures/testtools_lighttool.pngbin0 -> 1659 bytes-rw-r--r--games/devtest/mods/unittests/init.lua2
-rw-r--r--games/devtest/mods/unittests/itemdescription.lua51
-rw-r--r--games/devtest/mods/unittests/mod.conf1
-rw-r--r--games/devtest/mods/unittests/textures/default_dirt.pngbin0 -> 790 bytes-rw-r--r--games/devtest/mods/util_commands/init.lua66
-rw-r--r--lib/jsoncpp/json/UPDATING2
-rw-r--r--lib/jsoncpp/json/json-forwards.h366
-rw-r--r--lib/jsoncpp/json/json.h1626
-rw-r--r--lib/jsoncpp/jsoncpp.cpp2762
-rw-r--r--lib/lua/CMakeLists.txt13
-rw-r--r--minetest.conf.example189
-rw-r--r--misc/debpkg-control4
-rw-r--r--misc/minetest.exe.manifest2
-rw-r--r--misc/net.minetest.minetest.appdata.xml2
-rw-r--r--po/ar/minetest.po1073
-rw-r--r--po/be/minetest.po940
-rw-r--r--po/bg/minetest.po (renamed from po/fil/minetest.po)729
-rw-r--r--po/ca/minetest.po608
-rw-r--r--po/cs/minetest.po771
-rw-r--r--po/da/minetest.po709
-rw-r--r--po/de/minetest.po1071
-rw-r--r--po/dv/minetest.po519
-rw-r--r--po/el/minetest.po504
-rw-r--r--po/eo/minetest.po961
-rw-r--r--po/es/minetest.po1096
-rw-r--r--po/et/minetest.po1057
-rw-r--r--po/eu/minetest.po613
-rw-r--r--po/fi/minetest.po (renamed from po/my/minetest.po)526
-rw-r--r--po/fr/minetest.po1103
-rw-r--r--po/gd/minetest.po5568
-rw-r--r--po/gl/minetest.po4426
-rw-r--r--po/he/minetest.po1712
-rw-r--r--po/hi/minetest.po605
-rw-r--r--po/hu/minetest.po861
-rw-r--r--po/id/minetest.po1015
-rw-r--r--po/it/minetest.po1166
-rw-r--r--po/ja/minetest.po969
-rw-r--r--po/ja_KS/minetest.po6323
-rw-r--r--po/jbo/minetest.po622
-rw-r--r--po/kk/minetest.po523
-rw-r--r--po/kn/minetest.po635
-rw-r--r--po/ko/minetest.po1969
-rw-r--r--po/ky/minetest.po563
-rw-r--r--po/lt/minetest.po685
-rw-r--r--po/lv/minetest.po566
-rw-r--r--po/minetest.pot474
-rw-r--r--po/ms/minetest.po1048
-rw-r--r--po/ms_Arab/minetest.po6892
-rw-r--r--po/nb/minetest.po743
-rw-r--r--po/nl/minetest.po1789
-rw-r--r--po/nn/minetest.po879
-rw-r--r--po/pl/minetest.po955
-rw-r--r--po/pt/minetest.po1354
-rw-r--r--po/pt_BR/minetest.po1779
-rw-r--r--po/ro/minetest.po823
-rw-r--r--po/ru/minetest.po1390
-rw-r--r--po/sk/minetest.po7505
-rw-r--r--po/sl/minetest.po913
-rw-r--r--po/sr_Cyrl/minetest.po598
-rw-r--r--po/sr_Latn/minetest.po (renamed from po/lo/minetest.po)676
-rw-r--r--po/sv/minetest.po620
-rw-r--r--po/sw/minetest.po767
-rw-r--r--po/th/minetest.po777
-rw-r--r--po/tr/minetest.po973
-rw-r--r--po/uk/minetest.po791
-rw-r--r--po/vi/minetest.po492
-rw-r--r--po/zh_CN/minetest.po1156
-rw-r--r--po/zh_TW/minetest.po882
-rw-r--r--src/CMakeLists.txt83
-rw-r--r--src/activeobject.h14
-rw-r--r--src/activeobjectmgr.h3
-rw-r--r--src/chat.cpp17
-rw-r--r--src/chat.h2
-rw-r--r--src/client/camera.cpp46
-rw-r--r--src/client/camera.h49
-rw-r--r--src/client/client.cpp48
-rw-r--r--src/client/client.h9
-rw-r--r--src/client/clientenvironment.cpp143
-rw-r--r--src/client/clientlauncher.cpp220
-rw-r--r--src/client/clientlauncher.h23
-rw-r--r--src/client/clientmap.cpp133
-rw-r--r--src/client/clientmap.h19
-rw-r--r--src/client/clientmedia.cpp3
-rw-r--r--src/client/clouds.cpp60
-rw-r--r--src/client/content_cao.cpp181
-rw-r--r--src/client/content_cao.h12
-rw-r--r--src/client/content_mapblock.cpp11
-rw-r--r--src/client/content_mapblock.h2
-rw-r--r--src/client/event_manager.h2
-rw-r--r--src/client/fontengine.cpp80
-rw-r--r--src/client/fontengine.h12
-rw-r--r--src/client/game.cpp478
-rw-r--r--src/client/game.h13
-rw-r--r--src/client/gameui.cpp33
-rw-r--r--src/client/gameui.h1
-rw-r--r--src/client/guiscalingfilter.cpp5
-rw-r--r--src/client/guiscalingfilter.h3
-rw-r--r--src/client/hud.cpp213
-rw-r--r--src/client/hud.h14
-rw-r--r--src/client/inputhandler.cpp159
-rw-r--r--src/client/inputhandler.h164
-rw-r--r--src/client/joystick_controller.cpp68
-rw-r--r--src/client/joystick_controller.h38
-rw-r--r--src/client/keycode.cpp3
-rw-r--r--src/client/keys.h8
-rw-r--r--src/client/localplayer.cpp2
-rw-r--r--src/client/mapblock_mesh.cpp116
-rw-r--r--src/client/mapblock_mesh.h15
-rw-r--r--src/client/mesh.cpp9
-rw-r--r--src/client/mesh.h7
-rw-r--r--src/client/mesh_generator_thread.cpp6
-rw-r--r--src/client/mesh_generator_thread.h2
-rw-r--r--src/client/minimap.cpp269
-rw-r--r--src/client/minimap.h51
-rw-r--r--src/client/render/core.cpp1
-rw-r--r--src/client/render/factory.cpp7
-rw-r--r--src/client/render/interlaced.cpp2
-rw-r--r--src/client/renderingengine.cpp2
-rw-r--r--src/client/shader.cpp617
-rw-r--r--src/client/shader.h23
-rw-r--r--src/client/sky.cpp225
-rw-r--r--src/client/sky.h17
-rw-r--r--src/client/sound_openal.cpp56
-rw-r--r--src/client/tile.cpp14
-rw-r--r--src/client/tile.h5
-rw-r--r--src/client/wieldmesh.cpp211
-rw-r--r--src/clientiface.cpp124
-rw-r--r--src/clientiface.h25
-rw-r--r--src/constants.h2
-rw-r--r--src/content/CMakeLists.txt1
-rw-r--r--src/content/packages.cpp69
-rw-r--r--src/content/packages.h52
-rw-r--r--src/content/subgames.cpp82
-rw-r--r--src/content/subgames.h8
-rw-r--r--src/content_nodemeta.cpp18
-rw-r--r--src/database/database-dummy.cpp23
-rw-r--r--src/database/database-dummy.h9
-rw-r--r--src/database/database-files.cpp124
-rw-r--r--src/database/database-files.h9
-rw-r--r--src/database/database-leveldb.cpp16
-rw-r--r--src/defaultsettings.cpp60
-rw-r--r--src/defaultsettings.h9
-rw-r--r--src/emerge.cpp13
-rw-r--r--src/emerge.h1
-rw-r--r--src/environment.cpp1
-rw-r--r--src/environment.h1
-rw-r--r--src/exceptions.h5
-rw-r--r--src/filesys.cpp58
-rw-r--r--src/filesys.h5
-rw-r--r--src/gameparams.h19
-rw-r--r--src/gui/CMakeLists.txt2
-rw-r--r--src/gui/StyleSpec.h163
-rw-r--r--src/gui/guiBox.cpp87
-rw-r--r--src/gui/guiBox.h11
-rw-r--r--src/gui/guiButton.cpp22
-rw-r--r--src/gui/guiButton.h1
-rw-r--r--src/gui/guiButtonItemImage.cpp1
-rw-r--r--src/gui/guiButtonItemImage.h1
-rw-r--r--src/gui/guiChatConsole.cpp9
-rw-r--r--src/gui/guiChatConsole.h6
-rw-r--r--src/gui/guiConfirmRegistration.cpp24
-rw-r--r--src/gui/guiEditBox.cpp911
-rw-r--r--src/gui/guiEditBox.h220
-rw-r--r--src/gui/guiEditBoxWithScrollbar.cpp878
-rw-r--r--src/gui/guiEditBoxWithScrollbar.h140
-rw-r--r--src/gui/guiEngine.cpp23
-rw-r--r--src/gui/guiEngine.h13
-rw-r--r--src/gui/guiFormSpecMenu.cpp600
-rw-r--r--src/gui/guiFormSpecMenu.h62
-rw-r--r--src/gui/guiHyperText.cpp2
-rw-r--r--src/gui/guiInventoryList.cpp2
-rw-r--r--src/gui/guiKeyChangeMenu.cpp4
-rw-r--r--src/gui/guiMainMenu.h1
-rw-r--r--src/gui/guiPasswordChange.cpp21
-rw-r--r--src/gui/guiScene.cpp266
-rw-r--r--src/gui/guiScene.h86
-rw-r--r--src/gui/guiScrollBar.cpp2
-rw-r--r--src/gui/guiScrollBar.h1
-rw-r--r--src/gui/guiScrollContainer.cpp12
-rw-r--r--src/gui/guiScrollContainer.h2
-rw-r--r--src/gui/guiSkin.cpp2
-rw-r--r--src/gui/guiTable.cpp27
-rw-r--r--src/gui/guiTable.h6
-rw-r--r--src/gui/guiVolumeChange.cpp2
-rw-r--r--src/gui/intlGUIEditBox.cpp1241
-rw-r--r--src/gui/intlGUIEditBox.h154
-rw-r--r--src/gui/modalMenu.cpp261
-rw-r--r--src/gui/modalMenu.h37
-rw-r--r--src/gui/touchscreengui.cpp3
-rw-r--r--src/gui/touchscreengui.h1
-rw-r--r--src/httpfetch.cpp66
-rw-r--r--src/httpfetch.h22
-rw-r--r--src/hud.cpp3
-rw-r--r--src/hud.h21
-rw-r--r--src/inventory.cpp23
-rw-r--r--src/inventory.h1
-rw-r--r--src/inventorymanager.cpp330
-rw-r--r--src/inventorymanager.h12
-rw-r--r--src/irr_ptr.h89
-rw-r--r--src/irrlicht_changes/CGUITTFont.h2
-rw-r--r--src/irrlicht_changes/irrUString.h4
-rw-r--r--src/itemdef.cpp61
-rw-r--r--src/itemdef.h1
-rw-r--r--src/itemstackmetadata.cpp17
-rw-r--r--src/light.cpp7
-rw-r--r--src/log.cpp11
-rw-r--r--src/log.h10
-rw-r--r--src/main.cpp32
-rw-r--r--src/map.cpp238
-rw-r--r--src/map.h33
-rw-r--r--src/map_settings_manager.cpp76
-rw-r--r--src/map_settings_manager.h5
-rw-r--r--src/mapblock.cpp20
-rw-r--r--src/mapblock.h11
-rw-r--r--src/mapgen/mapgen.cpp36
-rw-r--r--src/mapgen/mapgen.h10
-rw-r--r--src/mapgen/mapgen_carpathian.cpp9
-rw-r--r--src/mapgen/mapgen_flat.cpp73
-rw-r--r--src/mapgen/mapgen_flat.h22
-rw-r--r--src/mapgen/mapgen_fractal.cpp9
-rw-r--r--src/mapgen/mapgen_singlenode.cpp6
-rw-r--r--src/mapgen/mapgen_v5.cpp9
-rw-r--r--src/mapgen/mapgen_v6.cpp18
-rw-r--r--src/mapgen/mapgen_v6.h1
-rw-r--r--src/mapgen/mapgen_v7.cpp90
-rw-r--r--src/mapgen/mapgen_v7.h2
-rw-r--r--src/mapgen/mapgen_valleys.cpp9
-rw-r--r--src/mapgen/mg_ore.cpp6
-rw-r--r--src/mapgen/mg_ore.h47
-rw-r--r--src/mapgen/mg_schematic.cpp8
-rw-r--r--src/mapgen/treegen.cpp16
-rw-r--r--src/mapgen/treegen.h2
-rw-r--r--src/mapnode.cpp34
-rw-r--r--src/mapnode.h4
-rw-r--r--src/mapsector.h1
-rw-r--r--src/mtevent.h (renamed from src/event.h)0
-rw-r--r--src/nameidmapping.cpp4
-rw-r--r--src/network/CMakeLists.txt5
-rw-r--r--src/network/clientopcodes.cpp1
-rw-r--r--src/network/clientpackethandler.cpp57
-rw-r--r--src/network/connection.cpp5
-rw-r--r--src/network/connection.h11
-rw-r--r--src/network/connectionthreads.cpp31
-rw-r--r--src/network/networkexceptions.h8
-rw-r--r--src/network/networkpacket.cpp56
-rw-r--r--src/network/networkpacket.h3
-rw-r--r--src/network/networkprotocol.h33
-rw-r--r--src/network/serveropcodes.cpp1
-rw-r--r--src/network/serverpackethandler.cpp560
-rw-r--r--src/nodedef.cpp241
-rw-r--r--src/nodedef.h77
-rw-r--r--src/nodemetadata.cpp8
-rw-r--r--src/noise.cpp2
-rw-r--r--src/noise.h7
-rw-r--r--src/object_properties.cpp58
-rw-r--r--src/object_properties.h3
-rw-r--r--src/particles.cpp4
-rw-r--r--src/pathfinder.cpp6
-rw-r--r--src/player.h12
-rw-r--r--src/porting.cpp47
-rw-r--r--src/porting.h22
-rw-r--r--src/porting_android.cpp6
-rw-r--r--src/porting_android.h2
-rw-r--r--src/profiler.cpp2
-rw-r--r--src/remoteplayer.cpp116
-rw-r--r--src/remoteplayer.h10
-rw-r--r--src/script/common/c_content.cpp86
-rw-r--r--src/script/common/c_content.h3
-rw-r--r--src/script/common/c_converter.cpp45
-rw-r--r--src/script/common/c_converter.h2
-rw-r--r--src/script/common/c_internal.cpp20
-rw-r--r--src/script/common/c_internal.h20
-rw-r--r--src/script/common/helper.cpp60
-rw-r--r--src/script/common/helper.h8
-rw-r--r--src/script/cpp_api/s_async.cpp29
-rw-r--r--src/script/cpp_api/s_async.h6
-rw-r--r--src/script/cpp_api/s_entity.cpp26
-rw-r--r--src/script/cpp_api/s_entity.h1
-rw-r--r--src/script/cpp_api/s_node.cpp19
-rw-r--r--src/script/cpp_api/s_node.h1
-rw-r--r--src/script/cpp_api/s_player.cpp13
-rw-r--r--src/script/cpp_api/s_player.h1
-rw-r--r--src/script/cpp_api/s_security.cpp11
-rw-r--r--src/script/lua_api/l_base.cpp60
-rw-r--r--src/script/lua_api/l_base.h21
-rw-r--r--src/script/lua_api/l_camera.cpp3
-rw-r--r--src/script/lua_api/l_env.cpp254
-rw-r--r--src/script/lua_api/l_env.h13
-rw-r--r--src/script/lua_api/l_http.cpp29
-rw-r--r--src/script/lua_api/l_http.h4
-rw-r--r--src/script/lua_api/l_internal.h11
-rw-r--r--src/script/lua_api/l_inventory.cpp1
-rw-r--r--src/script/lua_api/l_item.cpp14
-rw-r--r--src/script/lua_api/l_item.h3
-rw-r--r--src/script/lua_api/l_localplayer.cpp4
-rw-r--r--src/script/lua_api/l_mainmenu.cpp259
-rw-r--r--src/script/lua_api/l_mainmenu.h12
-rw-r--r--src/script/lua_api/l_mapgen.cpp13
-rw-r--r--src/script/lua_api/l_metadata.cpp8
-rw-r--r--src/script/lua_api/l_minimap.cpp24
-rw-r--r--src/script/lua_api/l_noise.cpp2
-rw-r--r--src/script/lua_api/l_object.cpp1362
-rw-r--r--src/script/lua_api/l_object.h68
-rw-r--r--src/script/lua_api/l_server.cpp124
-rw-r--r--src/script/lua_api/l_server.h2
-rw-r--r--src/script/lua_api/l_settings.cpp2
-rw-r--r--src/script/lua_api/l_util.cpp18
-rw-r--r--src/script/lua_api/l_util.h6
-rw-r--r--src/script/scripting_mainmenu.cpp1
-rw-r--r--src/server.cpp262
-rw-r--r--src/server.h59
-rw-r--r--src/server/activeobjectmgr.cpp15
-rw-r--r--src/server/activeobjectmgr.h3
-rw-r--r--src/server/luaentity_sao.cpp56
-rw-r--r--src/server/luaentity_sao.h6
-rw-r--r--src/server/mods.cpp13
-rw-r--r--src/server/mods.h8
-rw-r--r--src/server/player_sao.cpp55
-rw-r--r--src/server/player_sao.h4
-rw-r--r--src/server/serveractiveobject.cpp18
-rw-r--r--src/server/serveractiveobject.h62
-rw-r--r--src/server/unit_sao.cpp23
-rw-r--r--src/server/unit_sao.h5
-rw-r--r--src/serverenvironment.cpp117
-rw-r--r--src/serverenvironment.h18
-rw-r--r--src/serverlist.cpp183
-rw-r--r--src/serverlist.h13
-rw-r--r--src/settings.cpp394
-rw-r--r--src/settings.h91
-rw-r--r--src/settings_translation_file.cpp68
-rw-r--r--src/sound.h4
-rw-r--r--src/staticobject.cpp4
-rw-r--r--src/texture_override.cpp38
-rw-r--r--src/texture_override.h18
-rw-r--r--src/threading/thread.cpp69
-rw-r--r--src/threading/thread.h12
-rw-r--r--src/tool.cpp8
-rw-r--r--src/translation.cpp23
-rw-r--r--src/translation.h5
-rw-r--r--src/unittest/test.cpp4
-rw-r--r--src/unittest/test_connection.cpp35
-rw-r--r--src/unittest/test_map_settings_manager.cpp112
-rw-r--r--src/unittest/test_serialization.cpp374
-rw-r--r--src/unittest/test_servermodmanager.cpp2
-rw-r--r--src/unittest/test_settings.cpp89
-rw-r--r--src/unittest/test_threading.cpp25
-rw-r--r--src/unittest/test_utilities.cpp19
-rw-r--r--src/util/Optional.h77
-rw-r--r--src/util/base64.cpp5
-rw-r--r--src/util/md32_common.h2
-rw-r--r--src/util/serialize.cpp412
-rw-r--r--src/util/serialize.h252
-rw-r--r--src/util/sha256.c6
-rw-r--r--src/util/srp.cpp4
-rw-r--r--src/util/string.cpp186
-rw-r--r--src/util/string.h42
-rw-r--r--src/version.cpp5
-rw-r--r--textures/base/pack/cdb_add.pngbin0 -> 147 bytes-rw-r--r--textures/base/pack/cdb_clear.pngbin0 -> 182 bytes-rw-r--r--textures/base/pack/cdb_downloading.pngbin0 -> 201 bytes-rw-r--r--textures/base/pack/cdb_queued.pngbin0 -> 210 bytes-rw-r--r--textures/base/pack/cdb_update.pngbin0 -> 173 bytes-rw-r--r--textures/base/pack/cdb_viewonline.pngbin0 -> 191 bytes-rw-r--r--textures/base/pack/clear.pngbin0 -> 708 bytes-rw-r--r--textures/base/pack/no_texture_airlike.pngbin0 -> 178 bytes-rw-r--r--textures/base/pack/search.pngbin0 -> 1908 bytes-rwxr-xr-xutil/bump_version.sh22
-rw-r--r--util/ci/clang-format-whitelist.txt2
-rw-r--r--util/ci/common.sh6
-rwxr-xr-x[-rw-r--r--]util/ci/lint.sh0
-rwxr-xr-xutil/gather_git_credits.py67
-rw-r--r--util/wireshark/minetest.lua35
489 files changed, 53781 insertions, 50231 deletions
diff --git a/.clang-format b/.clang-format
index dc7380ffd..0db8ab167 100644
--- a/.clang-format
+++ b/.clang-format
@@ -29,3 +29,4 @@ AlignAfterOpenBracket: DontAlign
ContinuationIndentWidth: 16
ConstructorInitializerIndentWidth: 16
BreakConstructorInitializers: AfterColon
+AlwaysBreakTemplateDeclarations: Yes
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index b234fb283..b01a89509 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -43,6 +43,12 @@ Contributions are welcome! Here's how you can help:
4. The code's interfaces are well designed, regardless of other aspects that might need more work in the future.
5. It uses protocols and formats which include the required compatibility.
+### Important note about automated GitHub checks
+
+When you submit a pull request, GitHub automatically runs checks on the Minetest Engine combined with your changes. One of these checks is called 'cpp lint / clang format', which checks code formatting. Because formatting for readability requires human judgement this check often fails and often makes unsuitable formatting requests which make code readability worse.
+
+If this check fails, look at the details to check for any clear mistakes and correct those. However, you should not apply everything ClangFormat requests. Ignore requests that make code readability worse and any other clearly unsuitable requests. Discuss in the pull request with a core developer about how to progress.
+
## Issues
If you experience an issue, we would like to know the details - especially when a stable release is on the way.
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
new file mode 100644
index 000000000..0fcfe2390
--- /dev/null
+++ b/.github/workflows/android.yml
@@ -0,0 +1,42 @@
+name: android
+
+# build on c/cpp changes or workflow changes
+on:
+ push:
+ paths:
+ - 'lib/**.[ch]'
+ - 'lib/**.cpp'
+ - 'src/**.[ch]'
+ - 'src/**.cpp'
+ - 'build/android/**'
+ - '.github/workflows/android.yml'
+ pull_request:
+ paths:
+ - 'lib/**.[ch]'
+ - 'lib/**.cpp'
+ - 'src/**.[ch]'
+ - 'src/**.cpp'
+ - 'build/android/**'
+ - '.github/workflows/android.yml'
+
+jobs:
+ build:
+ runs-on: ubuntu-18.04
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Build with Gradle
+ run: cd build/android; ./gradlew assemblerelease
+ - name: Save armeabi artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: Minetest-armeabi-v7a.apk
+ path: build/android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk
+ - name: Save arm64 artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: Minetest-arm64-v8a.apk
+ path: build/android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ae359f5d8..a3cc92a8e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -33,9 +33,8 @@ jobs:
- uses: actions/checkout@v2
- name: Install deps
run: |
- sudo apt-get install g++-6 gcc-6 -qyy
source ./util/ci/common.sh
- install_linux_deps
+ install_linux_deps g++-6
- name: Build
run: |
@@ -55,9 +54,8 @@ jobs:
- uses: actions/checkout@v2
- name: Install deps
run: |
- sudo apt-get install g++-8 gcc-8 -qyy
source ./util/ci/common.sh
- install_linux_deps
+ install_linux_deps g++-8
- name: Build
run: |
@@ -77,9 +75,8 @@ jobs:
- uses: actions/checkout@v2
- name: Install deps
run: |
- sudo apt-get install clang-3.9 -qyy
source ./util/ci/common.sh
- install_linux_deps
+ install_linux_deps clang-3.9
- name: Build
run: |
@@ -99,11 +96,8 @@ jobs:
- uses: actions/checkout@v2
- name: Install deps
run: |
- sudo apt-get install clang-9 valgrind -qyy
source ./util/ci/common.sh
- install_linux_deps
- env:
- WITH_LUAJIT: 1
+ install_linux_deps clang-9 valgrind libluajit-5.1-dev
- name: Build
run: |
@@ -111,6 +105,7 @@ jobs:
env:
CC: clang-9
CXX: clang++-9
+ CMAKE_FLAGS: "-DREQUIRE_LUAJIT=1"
- name: Test
run: |
@@ -128,9 +123,8 @@ jobs:
- uses: actions/checkout@v2
- name: Install deps
run: |
- sudo apt-get install clang-9 -qyy
source ./util/ci/common.sh
- install_linux_deps
+ install_linux_deps clang-9
- name: Build prometheus-cpp
run: |
@@ -156,9 +150,8 @@ jobs:
- uses: actions/checkout@v2
- name: Install deps
run: |
- sudo apt-get install clang-9 -qyy
source ./util/ci/common.sh
- install_linux_deps
+ install_linux_deps clang-9
- name: Build
run: |
@@ -188,7 +181,7 @@ jobs:
- uses: actions/checkout@v2
- name: Install compiler
run: |
- sudo apt-get install gettext -qyy
+ sudo apt-get update -q && sudo apt-get install gettext -qyy
wget http://minetest.kitsunemimi.pw/mingw-w64-i686_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
sudo tar -xaf mingw.tar.xz -C /usr
@@ -206,7 +199,7 @@ jobs:
- uses: actions/checkout@v2
- name: Install compiler
run: |
- sudo apt-get install gettext -qyy
+ sudo apt-get update -q && sudo apt-get install gettext -qyy
wget http://minetest.kitsunemimi.pw/mingw-w64-x86_64_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
sudo tar -xaf mingw.tar.xz -C /usr
@@ -221,8 +214,8 @@ jobs:
name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }}
runs-on: windows-2019
env:
- VCPKG_VERSION: c7ab9d3110813979a873b2dbac630a9ab79850dc
-# 2020.04
+ VCPKG_VERSION: 0bf3923f9fab4001c00f0f429682a0853b5749e0
+# 2020.11
vcpkg_packages: irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit
strategy:
fail-fast: false
@@ -248,7 +241,7 @@ jobs:
uses: actions/checkout@v2
- name: Restore from cache and run vcpkg
- uses: lukka/run-vcpkg@v2
+ uses: lukka/run-vcpkg@v5
with:
vcpkgArguments: ${{env.vcpkg_packages}}
vcpkgDirectory: '${{ github.workspace }}\vcpkg'
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d03b7b601..0441aeaa1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -18,12 +18,12 @@ variables:
- mkdir cmakebuild
- mkdir -p artifact/minetest/usr/
- cd cmakebuild
- - cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DBUILD_SERVER=TRUE ..
+ - cmake -DCMAKE_INSTALL_PREFIX=../artifact/minetest/usr/ -DCMAKE_BUILD_TYPE=Release -DRUN_IN_PLACE=FALSE -DENABLE_GETTEXT=TRUE -DENABLE_SYSTEM_JSONCPP=TRUE -DBUILD_SERVER=TRUE ..
- make -j2
- make install
artifacts:
when: on_success
- expire_in: 2h
+ expire_in: 1h
paths:
- artifact/*
@@ -34,15 +34,14 @@ variables:
- apt-get install -y git
- mkdir -p build/deb/minetest/DEBIAN/
- cp misc/debpkg-control build/deb/minetest/DEBIAN/control
- - cp -Rp artifact/minetest/usr build/deb/minetest/
+ - cp -a artifact/minetest/usr build/deb/minetest/
script:
- - git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest
- - rm -Rf build/deb/minetest/usr/share/minetest/games/minetest/.git
+ - git clone $MINETEST_GAME_REPO build/deb/minetest/usr/share/minetest/games/minetest_game
+ - rm -rf build/deb/minetest/usr/share/minetest/games/minetest/.git
- sed -i 's/DATEPLACEHOLDER/'$(date +%y.%m.%d)'/g' build/deb/minetest/DEBIAN/control
- sed -i 's/LEVELDB_PLACEHOLDER/'$LEVELDB_PKG'/g' build/deb/minetest/DEBIAN/control
- cd build/deb/ && dpkg-deb -b minetest/ && mv minetest.deb ../../
artifacts:
- when: on_success
expire_in: 90 day
paths:
- ./*.deb
@@ -51,44 +50,14 @@ variables:
stage: deploy
before_script:
- apt-get update -y
- - apt-get install -y libc6 libcurl3-gnutls libfreetype6 libirrlicht1.8 $LEVELDB_PKG liblua5.1-0 libluajit-5.1-2 libopenal1 libstdc++6 libvorbisfile3 libx11-6 zlib1g
script:
- - dpkg -i ./*.deb
+ - apt-get install -y ./*.deb
+ - minetest --version
##
## Debian
##
-# Jessie
-
-build:debian-8:
- extends: .build_template
- image: debian:8
- before_script:
- - echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" > /etc/apt/sources.list.d/uptodate-toolchain.list
- - apt-key adv --keyserver keyserver.ubuntu.com --recv BA9EF27F
- - apt-get update -y
- - apt-get -y install build-essential gcc-6 g++-6 libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
- variables:
- CC: gcc-6
- CXX: g++-6
-
-package:debian-8:
- extends: .debpkg_template
- image: debian:8
- dependencies:
- - build:debian-8
- variables:
- LEVELDB_PKG: libleveldb1
-
-deploy:debian-8:
- extends: .debpkg_install
- image: debian:8
- dependencies:
- - package:debian-8
- variables:
- LEVELDB_PKG: libleveldb1
-
# Stretch
build:debian-9:
@@ -101,7 +70,7 @@ build:debian-9:
package:debian-9:
extends: .debpkg_template
image: debian:9
- dependencies:
+ needs:
- build:debian-9
variables:
LEVELDB_PKG: libleveldb1v5
@@ -109,12 +78,10 @@ package:debian-9:
deploy:debian-9:
extends: .debpkg_install
image: debian:9
- dependencies:
+ needs:
- package:debian-9
- variables:
- LEVELDB_PKG: libleveldb1v5
-# Stretch
+# Buster
build:debian-10:
extends: .build_template
@@ -126,7 +93,7 @@ build:debian-10:
package:debian-10:
extends: .debpkg_template
image: debian:10
- dependencies:
+ needs:
- build:debian-10
variables:
LEVELDB_PKG: libleveldb1d
@@ -134,44 +101,13 @@ package:debian-10:
deploy:debian-10:
extends: .debpkg_install
image: debian:10
- dependencies:
+ needs:
- package:debian-10
- variables:
- LEVELDB_PKG: libleveldb1d
+
##
## Ubuntu
##
-# Trusty
-
-build:ubuntu-14.04:
- extends: .build_template
- image: ubuntu:trusty
- before_script:
- - echo "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" > /etc/apt/sources.list.d/uptodate-toolchain.list
- - apt-key adv --keyserver keyserver.ubuntu.com --recv BA9EF27F
- - apt-get update -y
- - apt-get -y install build-essential gcc-6 g++-6 libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
- variables:
- CC: gcc-6
- CXX: g++-6
-
-package:ubuntu-14.04:
- extends: .debpkg_template
- image: ubuntu:trusty
- dependencies:
- - build:ubuntu-14.04
- variables:
- LEVELDB_PKG: libleveldb1
-
-deploy:ubuntu-14.04:
- extends: .debpkg_install
- image: ubuntu:trusty
- dependencies:
- - package:ubuntu-14.04
- variables:
- LEVELDB_PKG: libleveldb1
-
# Xenial
build:ubuntu-16.04:
@@ -184,7 +120,7 @@ build:ubuntu-16.04:
package:ubuntu-16.04:
extends: .debpkg_template
image: ubuntu:xenial
- dependencies:
+ needs:
- build:ubuntu-16.04
variables:
LEVELDB_PKG: libleveldb1v5
@@ -192,25 +128,45 @@ package:ubuntu-16.04:
deploy:ubuntu-16.04:
extends: .debpkg_install
image: ubuntu:xenial
- dependencies:
+ needs:
- package:ubuntu-16.04
+
+# Bionic
+
+build:ubuntu-18.04:
+ extends: .build_template
+ image: ubuntu:bionic
+ before_script:
+ - apt-get update -y
+ - apt-get -y install build-essential libirrlicht-dev cmake libbz2-dev libpng-dev libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev
+
+package:ubuntu-18.04:
+ extends: .debpkg_template
+ image: ubuntu:bionic
+ needs:
+ - build:ubuntu-18.04
variables:
LEVELDB_PKG: libleveldb1v5
+deploy:ubuntu-18.04:
+ extends: .debpkg_install
+ image: ubuntu:bionic
+ needs:
+ - package:ubuntu-18.04
+
##
## Fedora
##
-# Do we need to support this old version ?
-build:fedora-24:
+# Fedora 28 <-> RHEL 8
+build:fedora-28:
extends: .build_template
- image: fedora:24
+ image: fedora:28
before_script:
- - dnf -y install make automake gcc gcc-c++ kernel-devel cmake libcurl* openal* libvorbis* libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel bzip2-libs gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel doxygen spatialindex-devel bzip2-devel
-
+ - dnf -y install make automake gcc gcc-c++ kernel-devel cmake libcurl-devel openal-soft-devel libvorbis-devel libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel spatialindex-devel
##
-## Mingw for Windows
+## MinGW for Windows
##
.generic_win_template:
@@ -218,68 +174,63 @@ build:fedora-24:
before_script:
- apt-get update -y
- apt-get install -y wget xz-utils unzip git cmake gettext
- - wget -q http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
+ - wget -nv http://minetest.kitsunemimi.pw/mingw-w64-${WIN_ARCH}_9.2.0_ubuntu18.04.tar.xz -O mingw.tar.xz
- tar -xaf mingw.tar.xz -C /usr
.build_win_template:
extends: .generic_win_template
stage: build
artifacts:
- when: on_success
- expire_in: 2h
+ expire_in: 1h
paths:
- - build/*
+ - build/minetest/_build/*
.package_win_template:
extends: .generic_win_template
stage: package
script:
- - cd build/minetest/_build
- - make package
- - cd ../../../
- - mkdir minetest-win-${WIN_ARCH}
- - unzip build/minetest/_build/minetest-*-win*.zip -d minetest-win-${WIN_ARCH}
- - cp /usr/${WIN_ARCH}-w64-mingw32/bin/libgcc*.dll minetest-win-${WIN_ARCH}/minetest-*-win*/bin
- - cp /usr/${WIN_ARCH}-w64-mingw32/bin/libstdc++*.dll minetest-win-${WIN_ARCH}/minetest-*-win*/bin
- - cp /usr/${WIN_ARCH}-w64-mingw32/bin/libwinpthread*.dll minetest-win-${WIN_ARCH}/minetest-*-win*/bin
+ - unzip build/minetest/_build/minetest-*.zip
+ - cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libgcc*.dll minetest-*-win*/bin/
+ - cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libstdc++*.dll minetest-*-win*/bin/
+ - cp -p /usr/${WIN_ARCH}-w64-mingw32/bin/libwinpthread*.dll minetest-*-win*/bin/
artifacts:
- when: on_success
expire_in: 90 day
paths:
- - minetest-win-*/*
+ - minetest-*-win*/*
build:win32:
extends: .build_win_template
script:
- ./util/buildbot/buildwin32.sh build
variables:
- NO_PACKAGE: "1"
WIN_ARCH: "i686"
package:win32:
extends: .package_win_template
- dependencies:
+ needs:
- build:win32
variables:
- NO_PACKAGE: "1"
WIN_ARCH: "i686"
+
build:win64:
extends: .build_win_template
script:
- ./util/buildbot/buildwin64.sh build
variables:
- NO_PACKAGE: "1"
WIN_ARCH: "x86_64"
package:win64:
extends: .package_win_template
- dependencies:
+ needs:
- build:win64
variables:
- NO_PACKAGE: "1"
WIN_ARCH: "x86_64"
+##
+## Docker
+##
+
package:docker:
stage: package
image: docker:stable
@@ -293,6 +244,10 @@ package:docker:
- docker push ${CONTAINER_IMAGE}/server:$CI_COMMIT_REF_NAME
- docker push ${CONTAINER_IMAGE}/server:latest
+##
+## Gitlab Pages (Lua API documentation)
+##
+
pages:
stage: deploy
image: python:3.8
@@ -308,3 +263,31 @@ pages:
only:
- master
+##
+## AppImage
+##
+
+package:appimage-client:
+ stage: package
+ image: appimagecrafters/appimage-builder
+ needs:
+ - build:ubuntu-18.04
+ before_script:
+ - apt-get update -y
+ - apt-get install -y git wget
+ # Collect files
+ - mkdir AppDir
+ - cp -a artifact/minetest/usr/ AppDir/usr/
+ - rm AppDir/usr/bin/minetestserver
+ - cp -a clientmods AppDir/usr/share/minetest
+ script:
+ - git clone $MINETEST_GAME_REPO AppDir/usr/share/minetest/games/minetest_game
+ - rm -rf AppDir/usr/share/minetest/games/minetest/.git
+ - export VERSION=$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA
+ # Remove PrefersNonDefaultGPU property due to validation errors
+ - sed -i '/PrefersNonDefaultGPU/d' AppDir/usr/share/applications/net.minetest.minetest.desktop
+ - appimage-builder --skip-test
+ artifacts:
+ expire_in: 90 day
+ paths:
+ - ./*.AppImage
diff --git a/.luacheckrc b/.luacheckrc
index 3ab6e10c8..e010ab95c 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -72,3 +72,11 @@ files["builtin/mainmenu"] = {
"PLATFORM",
},
}
+
+files["builtin/common/tests"] = {
+ read_globals = {
+ "describe",
+ "it",
+ "assert",
+ },
+}
diff --git a/.mailmap b/.mailmap
index c487460a0..fcc763411 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,33 +1,67 @@
+# Documentation: https://git-scm.com/docs/git-check-mailmap#_mapping_authors
+
0gb.us <0gb.us@0gb.us> <us_0gb@laptop-0gb-us.0gb.us>
-Calinou <calinou9999@gmail.com> <calinou9999spam@gmail.com>
-Perttu Ahola <celeron55@gmail.com> celeron55 <celeron55@gmail.com>
+Calinou <calinou@opmbx.org> <calinou9999@gmail.com>
+Calinou <calinou@opmbx.org> <calinou9999spam@gmail.com>
+Perttu Ahola <celeron55@gmail.com>
Perttu Ahola <celeron55@gmail.com> celeron55 <celeron55@armada.(none)>
-Craig Robbins <kde.psych@gmail.com> <crobbins@localhost.localdomain>
+Zeno- <kde.psych@gmail.com>
+Zeno- <kde.psych@gmail.com> <crobbins@localhost.localdomain>
+Diego Martínez <kaeza@users.sf.net>
Diego Martínez <kaeza@users.sf.net> <lkaezadl3@gmail.com>
+Ilya Zhuravlev <zhuravlevilya@ya.ru>
Ilya Zhuravlev <zhuravlevilya@ya.ru> <whatever@xyz.is>
kwolekr <kwolekr@minetest.net> <mirrorisim@gmail.com>
-PilzAdam <pilzadam@minetest.net> PilzAdam <adam-k@outlook.com>
-PilzAdam <pilzadam@minetest.net> Pilz Adam <PilzAdam@gmx.de>
-PilzAdam <pilzadam@minetest.net> PilzAdam <PilzAdam@gmx.de>
+PilzAdam <pilzadam@minetest.net> <adam-k@outlook.com>
+PilzAdam <pilzadam@minetest.net> <PilzAdam@gmx.de>
proller <proller@github.com> <proler@github.com>
proller <proller@github.com> <proler@gmail.com>
RealBadAngel <maciej.kasatkin@o2.pl> <mk@realbadangel.pl>
RealBadAngel <maciej.kasatkin@o2.pl> <maciej.kasatkin@yahoo.com>
Selat <LongExampleTestName@gmail.com> <LongExampletestName@gmail.com>
ShadowNinja <shadowninja@minetest.net> ShadowNinja <noreply@gmail.com>
-Shen Zheyu <arsdragonfly@gmail.com> arsdragonfly <arsdragonfly@gmail.com>
-Pavel Elagin <elagin.pasha@gmail.com> elagin <elagin.pasha@gmail.com>
-Esteban I. Ruiz Moreno <exio4.com@gmail.com> Esteban I. RM <exio4.com@gmail.com>
-manuel duarte <ffrogger0@yahoo.com> manuel joaquim <ffrogger0@yahoo.com>
-manuel duarte <ffrogger0@yahoo.com> sweetbomber <ffrogger _zero_ at yahoo dot com>
-Diego Martínez <kaeza@users.sf.net> kaeza <kaeza@users.sf.net>
-Diego Martínez <kaeza@users.sf.net> Diego Martinez <kaeza@users.sf.net>
-Lord James <neftali_dtctv@hotmail.com> Lord89James <neftali_dtctv@hotmail.com>
-BlockMen <nmuelll@web.de> Block Men <nmuelll@web.de>
-sfan5 <sfan5@live.de> Sfan5 <sfan5@live.de>
-DannyDark <the_skeleton_of_a_child@yahoo.co.uk> dannydark <the_skeleton_of_a_child@yahoo.co.uk>
-Ilya Pavlov <TTChangeTheWorld@gmail.com> Ilya <TTChangeTheWorld@gmail.com>
-Ilya Zhuravlev <zhuravlevilya@ya.ru> xyzz <zhuravlevilya@ya.ru>
+Esteban I. Ruiz Moreno <exio4.com@gmail.com>
+Esteban I. Ruiz Moreno <exio4.com@gmail.com> <me@exio4.xyz>
+Lord James <neftali_dtctv@hotmail.com>
+BlockMen <nmuelll@web.de>
+sfan5 <sfan5@live.de>
+DannyDark <the_skeleton_of_a_child@yahoo.co.uk>
+Ilya Pavlov <TTChangeTheWorld@gmail.com>
sapier <Sapier at GMX dot net> sapier <sapier AT gmx DOT net>
sapier <Sapier at GMX dot net> sapier <sapier at gmx dot net>
-
+SmallJoker <SmallJoker@users.noreply.github.com> <mk939@ymail.com>
+Loïc Blot <nerzhul@users.noreply.github.com>
+Loïc Blot <nerzhul@users.noreply.github.com> <loic.blot@unix-experience.fr>
+numzero <numzer0@yandex.ru> Vitaliy <numzer0@yandex.ru>
+numzero <numzer0@yandex.ru> <silverunicorn2011@yandex.ru>
+Jean-Patrick Guerrero <kilbith@users.noreply.github.com>
+Jean-Patrick Guerrero <kilbith@users.noreply.github.com> <jeanpatrick.guerrero@gmail.com>
+HybridDog <3192173+HybridDog@users.noreply.github.com> <ovvv@web.de>
+srfqi <muhammadrifqipriyosusanto@gmail.com>
+Dániel Juhász <juhdanad@gmail.com>
+rubenwardy <rw@rubenwardy.com>
+rubenwardy <rw@rubenwardy.com> <rubenwardy@gmail.com>
+Paul Ouellette <oue.paul18@gmail.com>
+Vanessa Dannenberg <vanessa.e.dannenberg@gmail.com> <vanessaezekowitz@gmail.com>
+ClobberXD <ClobberXD@gmail.com>
+ClobberXD <ClobberXD@gmail.com> <ClobberXD@protonmail.com>
+ClobberXD <ClobberXD@gmail.com> <36130650+ClobberXD@users.noreply.github.com>
+Auke Kok <sofar+github@foo-projects.org>
+Auke Kok <sofar+github@foo-projects.org> <sofar@foo-projects.org>
+Desour <vorunbekannt75@web.de>
+Nathanaël Courant <Ekdohibs@users.noreply.github.com> <nathanael.courant@laposte.net>
+Ezhh <owlecho@live.com>
+paramat <paramat@users.noreply.github.com>
+paramat <paramat@users.noreply.github.com> <mat.gregory@virginmedia.com>
+lhofhansl <lhofhansl@yahoo.com> <larsh@apache.org>
+red-001 <red-001@outlook.ie> <red-001@openmailbox.org>
+Wuzzy <wuzzy2@mail.ru> <Wuzzy2@mail.ru>
+Wuzzy <wuzzy2@mail.ru> <almikes@aol.com>
+Jordach <jordach.snelling@gmail.com>
+MoNTE48 <MoNTE48@mail.ua>
+v-rob <robinsonvincent89@gmail.com>
+v-rob <robinsonvincent89@gmail.com> <31123645+v-rob@users.noreply.github.com>
+EvidenceB <49488517+EvidenceBKidscode@users.noreply.github.com>
+gregorycu <gregory.currie@gmail.com>
+Rogier <rogier777@gmail.com>
+Rogier <rogier777@gmail.com> <Rogier-5@users.noreply.github.com>
diff --git a/AppImageBuilder.yml b/AppImageBuilder.yml
new file mode 100644
index 000000000..9ecad5d8e
--- /dev/null
+++ b/AppImageBuilder.yml
@@ -0,0 +1,51 @@
+version: 1
+
+AppDir:
+ path: ./AppDir
+
+ app_info:
+ id: minetest
+ name: Minetest
+ icon: minetest
+ version: !ENV ${VERSION}
+ exec: usr/bin/minetest
+ exec_args: $@
+ runtime:
+ env:
+ APPDIR_LIBRARY_PATH: $APPDIR/usr/lib/x86_64-linux-gnu
+
+ apt:
+ arch: amd64
+ sources:
+ - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic main universe
+ key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32'
+ - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates main universe
+ - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-backports main universe
+ - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main universe
+
+ include:
+ - libirrlicht1.8
+ - libxxf86vm1
+ - libgl1-mesa-glx
+ - libsqlite3-0
+ - libogg0
+ - libvorbis0a
+ - libopenal1
+ - libcurl3-gnutls
+ - libfreetype6
+ - zlib1g
+ - libgmp10
+ - libjsoncpp1
+
+ files:
+ exclude:
+ - usr/share/man
+ - usr/share/doc/*/README.*
+ - usr/share/doc/*/changelog.*
+ - usr/share/doc/*/NEWS.*
+ - usr/share/doc/*/TODO.*
+
+AppImage:
+ update-information: None
+ sign-key: None
+ arch: x86_64
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8d33a2a7e..f6a0d22fe 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,22 +1,18 @@
-cmake_minimum_required(VERSION 2.6)
+cmake_minimum_required(VERSION 3.5)
-if(${CMAKE_VERSION} STREQUAL "2.8.2")
- # Bug http://vtk.org/Bug/view.php?id=11020
- message(WARNING "CMake/CPack version 2.8.2 will not create working .deb packages!")
-endif()
+cmake_policy(SET CMP0025 OLD)
# This can be read from ${PROJECT_NAME} after project() is called
project(minetest)
set(PROJECT_NAME_CAPITALIZED "Minetest")
-# Works only for cmake 3.1 and greater
set(CMAKE_CXX_STANDARD 11)
set(GCC_MINIMUM_VERSION "4.8")
set(CLANG_MINIMUM_VERSION "3.4")
# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
set(VERSION_MAJOR 5)
-set(VERSION_MINOR 3)
+set(VERSION_MINOR 4)
set(VERSION_PATCH 0)
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
@@ -165,7 +161,7 @@ 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/"
+install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game" DESTINATION "${SHAREDIR}/games/"
COMPONENT "SUBGAME_MINETEST_GAME" OPTIONAL PATTERN ".git*" EXCLUDE )
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/devtest" DESTINATION "${SHAREDIR}/games/"
COMPONENT "SUBGAME_MINIMAL" OPTIONAL PATTERN ".git*" EXCLUDE )
@@ -249,15 +245,15 @@ cpack_add_component(Docs
cpack_add_component(SUBGAME_MINETEST_GAME
DISPLAY_NAME "Minetest Game"
- DESCRIPTION "The official subgame for the Minetest engine, that can easily extended by mods."
- GROUP "Subgames"
+ DESCRIPTION "The default game bundled in the Minetest engine. Mainly used as a modding base."
+ GROUP "Games"
)
cpack_add_component(SUBGAME_MINIMAL
DISPLAY_NAME "Development Test"
- DESCRIPTION "A minimal test game helping to develop the engine."
+ DESCRIPTION "A basic testing environment used for engine development and sometimes for testing mods."
DISABLED #DISABLED does not mean it is disabled, and is just not selected by default.
- GROUP "Subgames"
+ GROUP "Games"
)
cpack_add_component_group(Subgames
@@ -278,19 +274,20 @@ if(WIN32)
set(CPACK_GENERATOR ZIP)
else()
- set(CPACK_GENERATOR WIX ZIP)
+ set(CPACK_GENERATOR WIX)
set(CPACK_PACKAGE_NAME "${PROJECT_NAME_CAPITALIZED}")
- set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME_CAPITALIZED}")
+ set(CPACK_PACKAGE_INSTALL_DIRECTORY ".")
set(CPACK_PACKAGE_EXECUTABLES ${PROJECT_NAME} "${PROJECT_NAME_CAPITALIZED}")
set(CPACK_CREATE_DESKTOP_LINKS ${PROJECT_NAME})
+ set(CPACK_PACKAGING_INSTALL_PREFIX "/${PROJECT_NAME_CAPITALIZED}")
set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/misc/minetest-icon.ico")
- # Supported languages can be found at
+ # Supported languages can be found at
# http://wixtoolset.org/documentation/manual/v3/wixui/wixui_localization.html
#set(CPACK_WIX_CULTURES "ar-SA,bg-BG,ca-ES,hr-HR,cs-CZ,da-DK,nl-NL,en-US,et-EE,fi-FI,fr-FR,de-DE")
set(CPACK_WIX_UI_BANNER "${CMAKE_CURRENT_SOURCE_DIR}/misc/CPACK_WIX_UI_BANNER.BMP")
set(CPACK_WIX_UI_DIALOG "${CMAKE_CURRENT_SOURCE_DIR}/misc/CPACK_WIX_UI_DIALOG.BMP")
-
+
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/doc/lgpl-2.1.txt")
# The correct way would be to include both x32 and x64 into one installer
diff --git a/Dockerfile b/Dockerfile
index 72343ab9c..871ca9825 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@ WORKDIR /usr/src/minetest
RUN apk add --no-cache git build-base irrlicht-dev cmake bzip2-dev libpng-dev \
jpeg-dev libxxf86vm-dev mesa-dev sqlite-dev libogg-dev \
libvorbis-dev openal-soft-dev curl-dev freetype-dev zlib-dev \
- gmp-dev jsoncpp-dev postgresql-dev ca-certificates && \
+ gmp-dev jsoncpp-dev postgresql-dev luajit-dev ca-certificates && \
git clone --depth=1 -b ${MINETEST_GAME_VERSION} https://github.com/minetest/minetest_game.git ./games/minetest_game && \
rm -fr ./games/minetest_game/.git
@@ -51,7 +51,7 @@ RUN mkdir build && \
FROM alpine:3.11
-RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq && \
+RUN apk add --no-cache sqlite-libs curl gmp libstdc++ libgcc libpq luajit && \
adduser -D minetest --uid 30000 -h /var/lib/minetest && \
chown -R minetest:minetest /var/lib/minetest
diff --git a/LICENSE.txt b/LICENSE.txt
index f5c51833b..9b8ee851a 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -11,6 +11,9 @@ http://creativecommons.org/licenses/by-sa/3.0/
textures/base/pack/refresh.png is under the Apache 2 license
https://www.apache.org/licenses/LICENSE-2.0.html
+Textures by Zughy are under CC BY-SA 4.0
+https://creativecommons.org/licenses/by-sa/4.0/
+
Authors of media files
-----------------------
Everything not listed in here:
@@ -23,6 +26,8 @@ paramat:
textures/base/pack/menu_header.png
textures/base/pack/next_icon.png
textures/base/pack/prev_icon.png
+ textures/base/pack/clear.png
+ textures/base/pack/search.png
rubenwardy, paramat:
textures/base/pack/start_icon.png
@@ -45,6 +50,14 @@ srifqi
textures/base/pack/joystick_off.png
textures/base/pack/minimap_btn.png
+Zughy:
+ textures/base/pack/cdb_add.png
+ textures/base/pack/cdb_clear.png
+ textures/base/pack/cdb_downloading.png
+ textures/base/pack/cdb_queued.png
+ textures/base/pack/cdb_update.png
+ textures/base/pack/cdb_viewonline.png
+
License of Minetest source code
-------------------------------
diff --git a/README.md b/README.md
index 6a3c11f40..58ec0c821 100644
--- a/README.md
+++ b/README.md
@@ -31,10 +31,10 @@ Table of Contents
Further documentation
----------------------
-- Website: http://minetest.net/
-- Wiki: http://wiki.minetest.net/
-- Developer wiki: http://dev.minetest.net/
-- Forum: http://forum.minetest.net/
+- Website: https://minetest.net/
+- Wiki: https://wiki.minetest.net/
+- Developer wiki: https://dev.minetest.net/
+- Forum: https://forum.minetest.net/
- GitHub: https://github.com/minetest/minetest/
- [doc/](doc/) directory of source distribution
@@ -314,13 +314,14 @@ It is highly recommended to use vcpkg as package manager.
After you successfully built vcpkg you can easily install the required libraries:
```powershell
-vcpkg install irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit --triplet x64-windows
+vcpkg install irrlicht zlib curl[winssl] openal-soft libvorbis libogg sqlite3 freetype luajit gmp jsoncpp --triplet x64-windows
```
- `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store.
- `openal-soft`, `libvorbis` and `libogg` are optional, but required to use sound.
- `freetype` is optional, it allows true-type font rendering.
- `luajit` is optional, it replaces the integrated Lua interpreter with a faster just-in-time interpreter.
+- `gmp` and `jsoncpp` are optional, otherwise the bundled versions will be compiled
There are other optional libraries, but they are not tested if they can build and link correctly.
@@ -353,7 +354,7 @@ This is outdated and not recommended. Follow the instructions on https://dev.min
Run the following script in PowerShell:
```powershell
-cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=0 -DENABLE_CURSES=0
+cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=OFF -DENABLE_CURSES=OFF -DENABLE_SYSTEM_JSONCPP=ON
cmake --build . --config Release
```
Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct.
diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle
index e3619af17..7f4eba8c4 100644
--- a/build/android/app/build.gradle
+++ b/build/android/app/build.gradle
@@ -1,8 +1,8 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
- buildToolsVersion '29.0.3'
- ndkVersion '21.1.6352462'
+ buildToolsVersion '30.0.3'
+ ndkVersion '22.0.7026061'
defaultConfig {
applicationId 'net.minetest.minetest'
minSdkVersion 16
@@ -11,8 +11,11 @@ android {
versionCode project.versionCode
}
+ // load properties
Properties props = new Properties()
- props.load(new FileInputStream(file('../local.properties')))
+ def propfile = file('../local.properties')
+ if (propfile.exists())
+ props.load(new FileInputStream(propfile))
if (props.getProperty('keystore') != null) {
signingConfigs {
@@ -61,10 +64,9 @@ task prepareAssets() {
copy {
from "${projRoot}/builtin" into "${assetsFolder}/builtin"
}
- /*copy {
- // ToDo: fix Minetest shaders that currently don't work with OpenGL ES
+ copy {
from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
- }*/
+ }
copy {
from "../native/deps/Android/Irrlicht/shaders" into "${assetsFolder}/client/shaders/Irrlicht"
}
@@ -107,5 +109,5 @@ android.applicationVariants.all { variant ->
dependencies {
implementation project(':native')
- implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
}
diff --git a/build/android/app/src/main/AndroidManifest.xml b/build/android/app/src/main/AndroidManifest.xml
index 0a7c8d95a..fa93e7069 100644
--- a/build/android/app/src/main/AndroidManifest.xml
+++ b/build/android/app/src/main/AndroidManifest.xml
@@ -17,8 +17,8 @@
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/label"
- android:resizeableActivity="false"
android:requestLegacyExternalStorage="true"
+ android:resizeableActivity="false"
tools:ignore="UnusedAttribute">
<meta-data
@@ -53,11 +53,6 @@
android:value="Minetest" />
</activity>
- <activity
- android:name=".InputDialogActivity"
- android:maxAspectRatio="3.0"
- android:theme="@style/InputTheme" />
-
<service
android:name=".UnzipService"
android:enabled="true"
diff --git a/build/android/app/src/main/java/net/minetest/minetest/CustomEditText.java b/build/android/app/src/main/java/net/minetest/minetest/CustomEditText.java
new file mode 100644
index 000000000..8d0a503d0
--- /dev/null
+++ b/build/android/app/src/main/java/net/minetest/minetest/CustomEditText.java
@@ -0,0 +1,45 @@
+/*
+Minetest
+Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
+Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+package net.minetest.minetest;
+
+import android.content.Context;
+import android.view.KeyEvent;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.appcompat.widget.AppCompatEditText;
+
+import java.util.Objects;
+
+public class CustomEditText extends AppCompatEditText {
+ public CustomEditText(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ InputMethodManager mgr = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ Objects.requireNonNull(mgr).hideSoftInputFromWindow(this.getWindowToken(), 0);
+ }
+ return false;
+ }
+}
diff --git a/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java
index 635512569..38a388230 100644
--- a/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java
+++ b/build/android/app/src/main/java/net/minetest/minetest/GameActivity.java
@@ -25,8 +25,16 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.text.InputType;
+import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import androidx.appcompat.app.AlertDialog;
+
+import java.util.Objects;
public class GameActivity extends NativeActivity {
static {
@@ -34,8 +42,8 @@ public class GameActivity extends NativeActivity {
System.loadLibrary("Minetest");
}
- private int messageReturnCode;
- private String messageReturnValue;
+ private int messageReturnCode = -1;
+ private String messageReturnValue = "";
public static native void putMessageBoxResult(String text);
@@ -43,8 +51,6 @@ public class GameActivity extends NativeActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- messageReturnCode = -1;
- messageReturnValue = "";
}
private void makeFullScreen() {
@@ -73,29 +79,46 @@ public class GameActivity extends NativeActivity {
// Ignore the back press so Minetest can handle it
}
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == 101) {
- if (resultCode == RESULT_OK) {
- String text = data.getStringExtra("text");
- messageReturnCode = 0;
- messageReturnValue = text;
- } else
- messageReturnCode = 1;
- }
+ public void showDialog(String acceptButton, String hint, String current, int editType) {
+ runOnUiThread(() -> showDialogUI(hint, current, editType));
}
- public void showDialog(String acceptButton, String hint, String current, int editType) {
- Intent intent = new Intent(this, InputDialogActivity.class);
- Bundle params = new Bundle();
- params.putString("acceptButton", acceptButton);
- params.putString("hint", hint);
- params.putString("current", current);
- params.putInt("editType", editType);
- intent.putExtras(params);
- startActivityForResult(intent, 101);
- messageReturnValue = "";
- messageReturnCode = -1;
+ private void showDialogUI(String hint, String current, int editType) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ EditText editText = new CustomEditText(this);
+ builder.setView(editText);
+ AlertDialog alertDialog = builder.create();
+ editText.requestFocus();
+ editText.setHint(hint);
+ editText.setText(current);
+ final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
+ Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED,
+ InputMethodManager.HIDE_IMPLICIT_ONLY);
+ if (editType == 1)
+ editText.setInputType(InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+ else if (editType == 3)
+ editText.setInputType(InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ else
+ editText.setInputType(InputType.TYPE_CLASS_TEXT);
+ editText.setSelection(editText.getText().length());
+ editText.setOnKeyListener((view, KeyCode, event) -> {
+ if (KeyCode == KeyEvent.KEYCODE_ENTER) {
+ imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
+ messageReturnCode = 0;
+ messageReturnValue = editText.getText().toString();
+ alertDialog.dismiss();
+ return true;
+ }
+ return false;
+ });
+ alertDialog.show();
+ alertDialog.setOnCancelListener(dialog -> {
+ getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+ messageReturnValue = current;
+ messageReturnCode = -1;
+ });
}
public int getDialogState() {
@@ -119,8 +142,8 @@ public class GameActivity extends NativeActivity {
return getResources().getDisplayMetrics().widthPixels;
}
- public void openURL(String url) {
- Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ public void openURI(String uri) {
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
startActivity(browserIntent);
}
}
diff --git a/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java b/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java
deleted file mode 100644
index 7c6aa111d..000000000
--- a/build/android/app/src/main/java/net/minetest/minetest/InputDialogActivity.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2020 MoNTE48, Maksim Gamarnik <MoNTE48@mail.ua>
-Copyright (C) 2014-2020 ubulem, Bektur Mambetov <berkut87@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-package net.minetest.minetest;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.text.InputType;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatActivity;
-
-import java.util.Objects;
-
-public class InputDialogActivity extends AppCompatActivity {
- private AlertDialog alertDialog;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Bundle b = getIntent().getExtras();
- int editType = Objects.requireNonNull(b).getInt("editType");
- String hint = b.getString("hint");
- String current = b.getString("current");
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- EditText editText = new EditText(this);
- builder.setView(editText);
- editText.requestFocus();
- editText.setHint(hint);
- editText.setText(current);
- final InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
- Objects.requireNonNull(imm).toggleSoftInput(InputMethodManager.SHOW_FORCED,
- InputMethodManager.HIDE_IMPLICIT_ONLY);
- if (editType == 3)
- editText.setInputType(InputType.TYPE_CLASS_TEXT |
- InputType.TYPE_TEXT_VARIATION_PASSWORD);
- else
- editText.setInputType(InputType.TYPE_CLASS_TEXT);
- editText.setOnKeyListener((view, KeyCode, event) -> {
- if (KeyCode == KeyEvent.KEYCODE_ENTER) {
- imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
- pushResult(editText.getText().toString());
- return true;
- }
- return false;
- });
- alertDialog = builder.create();
- if (!this.isFinishing())
- alertDialog.show();
- alertDialog.setOnCancelListener(dialog -> {
- pushResult(editText.getText().toString());
- setResult(Activity.RESULT_CANCELED);
- alertDialog.dismiss();
- makeFullScreen();
- finish();
- });
- }
-
- private void pushResult(String text) {
- Intent resultData = new Intent();
- resultData.putExtra("text", text);
- setResult(AppCompatActivity.RESULT_OK, resultData);
- alertDialog.dismiss();
- makeFullScreen();
- finish();
- }
-
- private void makeFullScreen() {
- if (Build.VERSION.SDK_INT >= 19)
- this.getWindow().getDecorView().setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
- }
-}
diff --git a/build/android/app/src/main/res/values/styles.xml b/build/android/app/src/main/res/values/styles.xml
index 618507e63..291a4eaf1 100644
--- a/build/android/app/src/main/res/values/styles.xml
+++ b/build/android/app/src/main/res/values/styles.xml
@@ -8,15 +8,8 @@
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="p">shortEdges</item>
</style>
- <style name="InputTheme" parent="Theme.AppCompat.DayNight.Dialog">
- <item name="windowNoTitle">true</item>
- <item name="android:windowBackground">@android:color/transparent</item>
- </style>
-
- <style name="CustomProgressBar" parent="@style/Widget.AppCompat.ProgressBar.Horizontal">
+ <style name="CustomProgressBar" parent="Widget.AppCompat.ProgressBar.Horizontal">
<item name="android:indeterminateOnly">false</item>
- <item name="android:minHeight">10dip</item>
- <item name="android:maxHeight">20dip</item>
</style>
</resources>
diff --git a/build/android/build.gradle b/build/android/build.gradle
index 8707b563c..be9eaada4 100644
--- a/build/android/build.gradle
+++ b/build/android/build.gradle
@@ -1,10 +1,10 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
project.ext.set("versionMajor", 5) // Version Major
-project.ext.set("versionMinor", 3) // Version Minor
+project.ext.set("versionMinor", 4) // Version Minor
project.ext.set("versionPatch", 0) // Version Patch
-project.ext.set("versionExtra", "-dev") // Version Extra
-project.ext.set("versionCode", 30) // Android Version Code
+project.ext.set("versionExtra", "") // Version Extra
+project.ext.set("versionCode", 32) // Android Version Code
// NOTE: +2 after each release!
// +1 for ARM and +1 for ARM64 APK's, because
// each APK must have a larger `versionCode` than the previous
@@ -15,8 +15,8 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.3'
- classpath 'org.ajoberstar.grgit:grgit-gradle:4.0.2'
+ classpath 'com.android.tools.build:gradle:4.1.1'
+ classpath 'de.undercouch:gradle-download-task:4.1.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -31,4 +31,5 @@ allprojects {
task clean(type: Delete) {
delete rootProject.buildDir
+ delete 'native/deps'
}
diff --git a/build/android/gradle/wrapper/gradle-wrapper.properties b/build/android/gradle/wrapper/gradle-wrapper.properties
index d612cf333..7fd9307d7 100644
--- a/build/android/gradle/wrapper/gradle-wrapper.properties
+++ b/build/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,2 +1,6 @@
-#Mon Apr 06 00:06:16 CEST 2020
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
+#Fri Jan 08 17:52:00 UTC 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-all.zip
diff --git a/build/android/native/build.gradle b/build/android/native/build.gradle
index cbd50db6a..8ea6347b3 100644
--- a/build/android/native/build.gradle
+++ b/build/android/native/build.gradle
@@ -1,16 +1,16 @@
apply plugin: 'com.android.library'
-import org.ajoberstar.grgit.Grgit
+apply plugin: 'de.undercouch.download'
android {
compileSdkVersion 29
- buildToolsVersion '29.0.3'
- ndkVersion '21.1.6352462'
+ buildToolsVersion '30.0.3'
+ ndkVersion '22.0.7026061'
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
externalNativeBuild {
ndkBuild {
- arguments '-j8',
+ arguments '-j' + Runtime.getRuntime().availableProcessors(),
"versionMajor=${versionMajor}",
"versionMinor=${versionMinor}",
"versionPatch=${versionPatch}",
@@ -45,15 +45,54 @@ android {
}
}
-task cloneGitRepo() {
- def destination = file('deps')
- if(!destination.exists()) {
- def grgit = Grgit.clone(
- dir: destination,
- uri: 'https://github.com/minetest/minetest_android_deps_binaries'
- )
- grgit.close()
+// get precompiled deps
+def folder = 'minetest_android_deps_binaries'
+
+task downloadDeps(type: Download) {
+ src 'https://github.com/minetest/' + folder + '/archive/master.zip'
+ dest new File(buildDir, 'deps.zip')
+ overwrite false
+}
+
+task getDeps(dependsOn: downloadDeps, type: Copy) {
+ def deps = file('deps')
+ def f = file("$buildDir/" + folder + "-master")
+
+ if (!deps.exists() && !f.exists()) {
+ from zipTree(downloadDeps.dest)
+ into buildDir
+ }
+
+ doLast {
+ if (!deps.exists()) {
+ file(f).renameTo(file(deps))
+ }
+ }
+}
+
+// get sqlite
+def sqlite_ver = '3340000'
+task downloadSqlite(dependsOn: getDeps, type: Download) {
+ src 'https://www.sqlite.org/2020/sqlite-amalgamation-' + sqlite_ver + '.zip'
+ dest new File(buildDir, 'sqlite.zip')
+ overwrite false
+}
+
+task getSqlite(dependsOn: downloadSqlite, type: Copy) {
+ def sqlite = file('deps/Android/sqlite')
+ def f = file("$buildDir/sqlite-amalgamation-" + sqlite_ver)
+
+ if (!sqlite.exists() && !f.exists()) {
+ from zipTree(downloadSqlite.dest)
+ into buildDir
+ }
+
+ doLast {
+ if (!sqlite.exists()) {
+ file(f).renameTo(file(sqlite))
+ }
}
}
-preBuild.dependsOn cloneGitRepo
+preBuild.dependsOn getDeps
+preBuild.dependsOn getSqlite
diff --git a/builtin/client/chatcommands.lua b/builtin/client/chatcommands.lua
index 5cb1b40bb..0e8d4dd03 100644
--- a/builtin/client/chatcommands.lua
+++ b/builtin/client/chatcommands.lua
@@ -23,6 +23,11 @@ core.register_on_sending_chat_message(function(message)
return true
end
+ -- Run core.registered_on_chatcommand callbacks.
+ if core.run_callbacks(core.registered_on_chatcommand, 5, cmd, param) then
+ return true
+ end
+
local cmd_def = core.registered_chatcommands[cmd]
if cmd_def then
core.set_last_run_mod(cmd_def.mod_origin)
diff --git a/builtin/client/register.lua b/builtin/client/register.lua
index c1b4965c1..27a6b02d9 100644
--- a/builtin/client/register.lua
+++ b/builtin/client/register.lua
@@ -4,6 +4,13 @@ core.callback_origins = {}
local getinfo = debug.getinfo
debug.getinfo = nil
+--- Runs given callbacks.
+--
+-- Note: this function is also called from C++
+-- @tparam table callbacks a table with registered callbacks, like `core.registered_on_*`
+-- @tparam number mode a RunCallbacksMode, as defined in src/script/common/c_internal.h
+-- @param ... arguments for the callback
+-- @return depends on mode
function core.run_callbacks(callbacks, mode, ...)
assert(type(callbacks) == "table")
local cb_len = #callbacks
@@ -63,6 +70,7 @@ core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration
core.registered_on_shutdown, core.register_on_shutdown = make_registration()
core.registered_on_receiving_chat_message, core.register_on_receiving_chat_message = make_registration()
core.registered_on_sending_chat_message, core.register_on_sending_chat_message = make_registration()
+core.registered_on_chatcommand, core.register_on_chatcommand = make_registration()
core.registered_on_death, core.register_on_death = make_registration()
core.registered_on_hp_modification, core.register_on_hp_modification = make_registration()
core.registered_on_damage_taken, core.register_on_damage_taken = make_registration()
diff --git a/builtin/common/after.lua b/builtin/common/after.lua
index b314711c9..e20f292f0 100644
--- a/builtin/common/after.lua
+++ b/builtin/common/after.lua
@@ -31,11 +31,13 @@ function core.after(after, func, ...)
assert(tonumber(after) and type(func) == "function",
"Invalid minetest.after invocation")
local expire = time + after
- jobs[#jobs + 1] = {
+ local new_job = {
func = func,
expire = expire,
arg = {...},
- mod_origin = core.get_last_run_mod()
+ mod_origin = core.get_last_run_mod(),
}
+ jobs[#jobs + 1] = new_job
time_next = math.min(time_next, expire)
+ return { cancel = function() new_job.func = function() end end }
end
diff --git a/builtin/common/information_formspecs.lua b/builtin/common/information_formspecs.lua
index 8afa5bc87..3e2f1f079 100644
--- a/builtin/common/information_formspecs.lua
+++ b/builtin/common/information_formspecs.lua
@@ -115,7 +115,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
return
end
- local event = minetest.explode_table_event(fields.list)
+ local event = core.explode_table_event(fields.list)
if event.type ~= "INV" then
local name = player:get_player_name()
core.show_formspec(name, "__builtin:help_cmds",
diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua
index e29a9f422..0f3897f47 100644
--- a/builtin/common/misc_helpers.lua
+++ b/builtin/common/misc_helpers.lua
@@ -697,3 +697,7 @@ function core.privs_to_string(privs, delim)
end
return table.concat(list, delim)
end
+
+function core.is_nan(number)
+ return number ~= number
+end
diff --git a/builtin/common/tests/vector_spec.lua b/builtin/common/tests/vector_spec.lua
index 6f308a4a8..0f287363a 100644
--- a/builtin/common/tests/vector_spec.lua
+++ b/builtin/common/tests/vector_spec.lua
@@ -44,6 +44,10 @@ describe("vector", function()
assert.same({ x = 2, y = 4, z = 6 }, vector.add(vector.new(1, 2, 3), { x = 1, y = 2, z = 3 }))
end)
+ it("offset()", function()
+ assert.same({ x = 41, y = 52, z = 63 }, vector.offset(vector.new(1, 2, 3), 40, 50, 60))
+ end)
+
-- This function is needed because of floating point imprecision.
local function almost_equal(a, b)
if type(a) == "number" then
diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua
index 1fd784ce2..d6437deda 100644
--- a/builtin/common/vector.lua
+++ b/builtin/common/vector.lua
@@ -137,6 +137,12 @@ function vector.divide(a, b)
end
end
+function vector.offset(v, x, y, z)
+ return {x = v.x + x,
+ y = v.y + y,
+ z = v.z + z}
+end
+
function vector.sort(a, b)
return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)},
{x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)}
diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua
index 6d26aabf0..976659ed3 100644
--- a/builtin/fstk/ui.lua
+++ b/builtin/fstk/ui.lua
@@ -18,6 +18,8 @@
ui = {}
ui.childlist = {}
ui.default = nil
+-- Whether fstk is currently showing its own formspec instead of active ui elements.
+ui.overridden = false
--------------------------------------------------------------------------------
function ui.add(child)
@@ -55,6 +57,7 @@ end
--------------------------------------------------------------------------------
function ui.update()
+ ui.overridden = false
local formspec = {}
-- handle errors
@@ -64,12 +67,14 @@ function ui.update()
formspec = {
"size[14,8]",
"real_coordinates[true]",
+ "set_focus[btn_reconnect_yes;true]",
"box[0.5,1.2;13,5;#000]",
("textarea[0.5,1.2;13,5;;%s;%s]"):format(
fgettext("The server has requested a reconnect:"), error_message),
"button[2,6.6;4,1;btn_reconnect_yes;" .. fgettext("Reconnect") .. "]",
"button[8,6.6;4,1;btn_reconnect_no;" .. fgettext("Main menu") .. "]"
}
+ ui.overridden = true
elseif gamedata ~= nil and gamedata.errormessage ~= nil then
local error_message = core.formspec_escape(gamedata.errormessage)
@@ -82,11 +87,13 @@ function ui.update()
formspec = {
"size[14,8]",
"real_coordinates[true]",
+ "set_focus[btn_error_confirm;true]",
"box[0.5,1.2;13,5;#000]",
("textarea[0.5,1.2;13,5;;%s;%s]"):format(
error_title, error_message),
"button[5,6.6;4,1;btn_error_confirm;" .. fgettext("OK") .. "]"
}
+ ui.overridden = true
else
local active_toplevel_ui_elements = 0
for key,value in pairs(ui.childlist) do
@@ -183,6 +190,16 @@ end
--------------------------------------------------------------------------------
core.event_handler = function(event)
+ -- Handle error messages
+ if ui.overridden then
+ if event == "MenuQuit" then
+ gamedata.errormessage = nil
+ gamedata.reconnect_requested = false
+ ui.update()
+ end
+ return
+ end
+
if ui.handle_events(event) then
ui.update()
return
diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua
index aae811794..945707623 100644
--- a/builtin/game/chat.lua
+++ b/builtin/game/chat.lua
@@ -58,6 +58,11 @@ core.register_on_chat_message(function(name, message)
param = param or ""
+ -- Run core.registered_on_chatcommands callbacks.
+ if core.run_callbacks(core.registered_on_chatcommands, 5, name, cmd, param) then
+ return true
+ end
+
local cmd_def = core.registered_chatcommands[cmd]
if not cmd_def then
core.chat_send_player(name, "-!- Invalid command: " .. cmd)
@@ -66,8 +71,17 @@ core.register_on_chat_message(function(name, message)
local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs)
if has_privs then
core.set_last_run_mod(cmd_def.mod_origin)
- local _, result = cmd_def.func(name, param)
- if result then
+ local success, result = cmd_def.func(name, param)
+ if success == false and result == nil then
+ core.chat_send_player(name, "-!- Invalid command usage")
+ local help_def = core.registered_chatcommands["help"]
+ if help_def then
+ local _, helpmsg = help_def.func(name, cmd)
+ if helpmsg then
+ core.chat_send_player(name, helpmsg)
+ end
+ end
+ elseif result then
core.chat_send_player(name, result)
end
else
@@ -1070,10 +1084,10 @@ core.register_chatcommand("last-login", {
local pauth = core.get_auth_handler().get_auth(param)
if pauth and pauth.last_login and pauth.last_login ~= -1 then
-- Time in UTC, ISO 8601 format
- return true, "Last login time was " ..
+ return true, param.."'s last login time was " ..
os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login)
end
- return false, "Last login time is unknown"
+ return false, param.."'s last login time is unknown"
end,
})
diff --git a/builtin/game/deprecated.lua b/builtin/game/deprecated.lua
index 20f0482eb..c5c7848f5 100644
--- a/builtin/game/deprecated.lua
+++ b/builtin/game/deprecated.lua
@@ -1,29 +1,6 @@
-- Minetest: builtin/deprecated.lua
--
--- Default material types
---
-local function digprop_err()
- core.log("deprecated", "The core.digprop_* functions are obsolete and need to be replaced by item groups.")
-end
-
-core.digprop_constanttime = digprop_err
-core.digprop_stonelike = digprop_err
-core.digprop_dirtlike = digprop_err
-core.digprop_gravellike = digprop_err
-core.digprop_woodlike = digprop_err
-core.digprop_leaveslike = digprop_err
-core.digprop_glasslike = digprop_err
-
-function core.node_metadata_inventory_move_allow_all()
- core.log("deprecated", "core.node_metadata_inventory_move_allow_all is obsolete and does nothing.")
-end
-
-function core.add_to_creative_inventory(itemstring)
- core.log("deprecated", "core.add_to_creative_inventory is obsolete and does nothing.")
-end
-
---
-- EnvRef
--
core.env = {}
@@ -77,7 +54,7 @@ core.setting_save = setting_proxy("write")
function core.register_on_auth_fail(func)
core.log("deprecated", "core.register_on_auth_fail " ..
- "is obsolete and should be replaced by " ..
+ "is deprecated and should be replaced by " ..
"core.register_on_authplayer instead.")
core.register_on_authplayer(function (player_name, ip, is_success)
diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua
index 714506a5f..057d0d0ed 100644
--- a/builtin/game/falling.lua
+++ b/builtin/game/falling.lua
@@ -52,6 +52,7 @@ core.register_entity(":__builtin:falling_node", {
floats = false,
set_node = function(self, node, meta)
+ node.param2 = node.param2 or 0
self.node = node
meta = meta or {}
if type(meta.to_table) == "function" then
@@ -83,6 +84,9 @@ core.register_entity(":__builtin:falling_node", {
local textures
if def.tiles and def.tiles[1] then
local tile = def.tiles[1]
+ if def.drawtype == "torchlike" and def.paramtype2 ~= "wallmounted" then
+ tile = def.tiles[2] or def.tiles[1]
+ end
if type(tile) == "table" then
tile = tile.name
end
@@ -126,7 +130,7 @@ core.register_entity(":__builtin:falling_node", {
-- Set collision box (certain nodeboxes only for now)
local nb_types = {fixed=true, leveled=true, connected=true}
if def.drawtype == "nodebox" and def.node_box and
- nb_types[def.node_box.type] then
+ nb_types[def.node_box.type] and def.node_box.fixed then
local box = table.copy(def.node_box.fixed)
if type(box[1]) == "table" then
box = #box == 1 and box[1] or nil -- We can only use a single box
@@ -143,9 +147,13 @@ core.register_entity(":__builtin:falling_node", {
-- Rotate entity
if def.drawtype == "torchlike" then
- self.object:set_yaw(math.pi*0.25)
- elseif (node.param2 ~= 0 and (def.wield_image == ""
- or def.wield_image == nil))
+ if def.paramtype2 == "wallmounted" then
+ self.object:set_yaw(math.pi*0.25)
+ else
+ self.object:set_yaw(-math.pi*0.25)
+ end
+ elseif ((node.param2 ~= 0 or def.drawtype == "nodebox" or def.drawtype == "mesh")
+ and (def.wield_image == "" or def.wield_image == nil))
or def.drawtype == "signlike"
or def.drawtype == "mesh"
or def.drawtype == "normal"
@@ -160,16 +168,30 @@ core.register_entity(":__builtin:falling_node", {
elseif (def.paramtype2 == "wallmounted" or def.paramtype2 == "colorwallmounted") then
local rot = node.param2 % 8
local pitch, yaw, roll = 0, 0, 0
- if rot == 1 then
- pitch, yaw = math.pi, math.pi
- elseif rot == 2 then
- pitch, yaw = math.pi/2, math.pi/2
- elseif rot == 3 then
- pitch, yaw = math.pi/2, -math.pi/2
- elseif rot == 4 then
- pitch, yaw = math.pi/2, math.pi
- elseif rot == 5 then
- pitch, yaw = math.pi/2, 0
+ if def.drawtype == "nodebox" or def.drawtype == "mesh" then
+ if rot == 0 then
+ pitch, yaw = math.pi/2, 0
+ elseif rot == 1 then
+ pitch, yaw = -math.pi/2, math.pi
+ elseif rot == 2 then
+ pitch, yaw = 0, math.pi/2
+ elseif rot == 3 then
+ pitch, yaw = 0, -math.pi/2
+ elseif rot == 4 then
+ pitch, yaw = 0, math.pi
+ end
+ else
+ if rot == 1 then
+ pitch, yaw = math.pi, math.pi
+ elseif rot == 2 then
+ pitch, yaw = math.pi/2, math.pi/2
+ elseif rot == 3 then
+ pitch, yaw = math.pi/2, -math.pi/2
+ elseif rot == 4 then
+ pitch, yaw = math.pi/2, math.pi
+ elseif rot == 5 then
+ pitch, yaw = math.pi/2, 0
+ end
end
if def.drawtype == "signlike" then
pitch = pitch - math.pi/2
@@ -178,7 +200,7 @@ core.register_entity(":__builtin:falling_node", {
elseif rot == 1 then
yaw = yaw - math.pi/2
end
- elseif def.drawtype == "mesh" or def.drawtype == "normal" then
+ elseif def.drawtype == "mesh" or def.drawtype == "normal" or def.drawtype == "nodebox" then
if rot >= 0 and rot <= 1 then
roll = roll + math.pi
else
diff --git a/builtin/game/features.lua b/builtin/game/features.lua
index a15475333..36ff1f0b0 100644
--- a/builtin/game/features.lua
+++ b/builtin/game/features.lua
@@ -17,6 +17,8 @@ core.features = {
area_store_persistent_ids = true,
pathfinder_works = true,
object_step_has_moveresult = true,
+ direct_velocity_on_players = true,
+ use_texture_alpha_string_modes = true,
}
function core.has_feature(arg)
diff --git a/builtin/game/item.lua b/builtin/game/item.lua
index f680ce0d4..b68177c22 100644
--- a/builtin/game/item.lua
+++ b/builtin/game/item.lua
@@ -551,12 +551,13 @@ function core.node_dig(pos, node, digger)
local diggername = user_name(digger)
local log = make_log(diggername)
local def = core.registered_nodes[node.name]
+ -- Copy pos because the callback could modify it
if def and (not def.diggable or
- (def.can_dig and not def.can_dig(pos, digger))) then
+ (def.can_dig and not def.can_dig(vector.new(pos), digger))) then
log("info", diggername .. " tried to dig "
.. node.name .. " which is not diggable "
.. core.pos_to_string(pos))
- return
+ return false
end
if core.is_protected(pos, diggername) then
@@ -565,7 +566,7 @@ function core.node_dig(pos, node, digger)
.. " at protected position "
.. core.pos_to_string(pos))
core.record_protection_violation(pos, diggername)
- return
+ return false
end
log('action', diggername .. " digs "
@@ -648,6 +649,8 @@ function core.node_dig(pos, node, digger)
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
callback(pos_copy, node_copy, digger)
end
+
+ return true
end
function core.itemstring_with_palette(item, palette_index)
@@ -675,7 +678,7 @@ end
-- Item definition defaults
--
-local default_stack_max = tonumber(minetest.settings:get("default_stack_max")) or 99
+local default_stack_max = tonumber(core.settings:get("default_stack_max")) or 99
core.nodedef_default = {
-- Item properties
@@ -704,10 +707,6 @@ core.nodedef_default = {
on_receive_fields = nil,
- on_metadata_inventory_move = core.node_metadata_inventory_move_allow_all,
- on_metadata_inventory_offer = core.node_metadata_inventory_offer_allow_all,
- on_metadata_inventory_take = core.node_metadata_inventory_take_allow_all,
-
-- Node properties
drawtype = "normal",
visual_scale = 1.0,
@@ -718,7 +717,6 @@ core.nodedef_default = {
-- {name="", backface_culling=true},
-- {name="", backface_culling=true},
--},
- alpha = 255,
post_effect_color = {a=0, r=0, g=0, b=0},
paramtype = "none",
paramtype2 = "none",
diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua
index 20dd18044..9b1b23bfd 100644
--- a/builtin/game/item_entity.lua
+++ b/builtin/game/item_entity.lua
@@ -54,8 +54,9 @@ core.register_entity(":__builtin:item", {
local max_count = stack:get_stack_max()
local count = math.min(stack:get_count(), max_count)
local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3)
- local def = core.registered_nodes[itemname]
- local glow = def and math.floor(def.light_source / 2 + 0.5)
+ local def = core.registered_items[itemname]
+ local glow = def and def.light_source and
+ math.floor(def.light_source / 2 + 0.5)
self.object:set_properties({
is_visible = true,
diff --git a/builtin/game/knockback.lua b/builtin/game/knockback.lua
index b5c4cbc5a..a937aa186 100644
--- a/builtin/game/knockback.lua
+++ b/builtin/game/knockback.lua
@@ -42,5 +42,5 @@ core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool
return -- barely noticeable, so don't even send
end
- player:add_player_velocity(kdir)
+ player:add_velocity(kdir)
end)
diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua
index 341e613c2..b8c5e16a9 100644
--- a/builtin/game/misc.lua
+++ b/builtin/game/misc.lua
@@ -151,6 +151,12 @@ function core.setting_get_pos(name)
end
+-- See l_env.cpp for the other functions
+function core.get_artificial_light(param1)
+ return math.floor(param1 / 16)
+end
+
+
-- To be overriden by protection mods
function core.is_protected(pos, name)
@@ -260,3 +266,26 @@ end
function core.cancel_shutdown_requests()
core.request_shutdown("", false, -1)
end
+
+
+-- Callback handling for dynamic_add_media
+
+local dynamic_add_media_raw = core.dynamic_add_media_raw
+core.dynamic_add_media_raw = nil
+function core.dynamic_add_media(filepath, callback)
+ local ret = dynamic_add_media_raw(filepath)
+ if ret == false then
+ return ret
+ end
+ if callback == nil then
+ core.log("deprecated", "Calling minetest.dynamic_add_media without "..
+ "a callback is deprecated and will stop working in future versions.")
+ else
+ -- At the moment async loading is not actually implemented, so we
+ -- immediately call the callback ourselves
+ for _, name in ipairs(ret) do
+ callback(name)
+ end
+ end
+ return true
+end
diff --git a/builtin/game/register.lua b/builtin/game/register.lua
index 1034d4f2b..1cff85813 100644
--- a/builtin/game/register.lua
+++ b/builtin/game/register.lua
@@ -320,13 +320,6 @@ for name in pairs(forbidden_item_names) do
register_alias_raw(name, "")
end
-
--- Obsolete:
--- Aliases for core.register_alias (how ironic...)
--- core.alias_node = core.register_alias
--- core.alias_tool = core.register_alias
--- core.alias_craftitem = core.register_alias
-
--
-- Built-in node definitions. Also defined in C.
--
@@ -584,6 +577,7 @@ core.unregister_biome = make_wrap_deregistration(core.register_biome,
core.clear_registered_biomes, core.registered_biomes)
core.registered_on_chat_messages, core.register_on_chat_message = make_registration()
+core.registered_on_chatcommands, core.register_on_chatcommand = make_registration()
core.registered_globalsteps, core.register_globalstep = make_registration()
core.registered_playerevents, core.register_playerevent = make_registration()
core.registered_on_mods_loaded, core.register_on_mods_loaded = make_registration()
@@ -612,6 +606,7 @@ core.registered_can_bypass_userlimit, core.register_can_bypass_userlimit = make_
core.registered_on_modchannel_message, core.register_on_modchannel_message = make_registration()
core.registered_on_player_inventory_actions, core.register_on_player_inventory_action = make_registration()
core.registered_allow_player_inventory_actions, core.register_allow_player_inventory_action = make_registration()
+core.registered_on_rightclickplayers, core.register_on_rightclickplayer = make_registration()
--
-- Compatibility for on_mapgen_init()
diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua
index d192029c5..db5087a16 100644
--- a/builtin/game/statbars.lua
+++ b/builtin/game/statbars.lua
@@ -84,8 +84,8 @@ local function update_builtin_statbars(player)
end
if hud.id_breathbar and (not show_breathbar or breath == breath_max) then
- minetest.after(1, function(player_name, breath_bar)
- local player = minetest.get_player_by_name(player_name)
+ core.after(1, function(player_name, breath_bar)
+ local player = core.get_player_by_name(player_name)
if player then
player:hud_remove(breath_bar)
end
diff --git a/builtin/init.lua b/builtin/init.lua
index 75bb3db85..89b1fdc64 100644
--- a/builtin/init.lua
+++ b/builtin/init.lua
@@ -39,9 +39,20 @@ if INIT == "game" then
assert(not core.get_http_api)
elseif INIT == "mainmenu" then
local mm_script = core.settings:get("main_menu_script")
+ local custom_loaded = false
if mm_script and mm_script ~= "" then
- dofile(mm_script)
- else
+ local testfile = io.open(mm_script, "r")
+ if testfile then
+ testfile:close()
+ dofile(mm_script)
+ custom_loaded = true
+ core.log("info", "Loaded custom main menu script: "..mm_script)
+ else
+ core.log("error", "Failed to load custom main menu script: "..mm_script)
+ core.log("info", "Falling back to default main menu script")
+ end
+ end
+ if not custom_loaded then
dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua")
end
elseif INIT == "async" then
diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua
index 782d6973f..cd896f9ec 100644
--- a/builtin/mainmenu/common.lua
+++ b/builtin/mainmenu/common.lua
@@ -62,24 +62,6 @@ function image_column(tooltip, flagname)
"5=" .. core.formspec_escape(defaulttexturedir .. "server_ping_1.png")
end
---------------------------------------------------------------------------------
-function order_favorite_list(list)
- local res = {}
- --orders the favorite list after support
- for i = 1, #list do
- local fav = list[i]
- if is_server_protocol_compat(fav.proto_min, fav.proto_max) then
- res[#res + 1] = fav
- end
- end
- for i = 1, #list do
- local fav = list[i]
- if not is_server_protocol_compat(fav.proto_min, fav.proto_max) then
- res[#res + 1] = fav
- end
- end
- return res
-end
--------------------------------------------------------------------------------
function render_serverlist_row(spec, is_favorite)
@@ -87,7 +69,7 @@ function render_serverlist_row(spec, is_favorite)
if spec.name then
text = text .. core.formspec_escape(spec.name:trim())
elseif spec.address then
- text = text .. spec.address:trim()
+ text = text .. core.formspec_escape(spec.address:trim())
if spec.port then
text = text .. ":" .. spec.port
end
@@ -164,35 +146,15 @@ end
--------------------------------------------------------------------------------
os.tempfolder = function()
- if core.settings:get("TMPFolder") then
- return core.settings:get("TMPFolder") .. DIR_DELIM .. "MT_" .. math.random(0,10000)
- end
-
- local filetocheck = os.tmpname()
- os.remove(filetocheck)
-
- -- luacheck: ignore
- -- https://blogs.msdn.microsoft.com/vcblog/2014/06/18/c-runtime-crt-features-fixes-and-breaking-changes-in-visual-studio-14-ctp1/
- -- The C runtime (CRT) function called by os.tmpname is tmpnam.
- -- Microsofts tmpnam implementation in older CRT / MSVC releases is defective.
- -- tmpnam return values starting with a backslash characterize this behavior.
- -- https://sourceforge.net/p/mingw-w64/bugs/555/
- -- MinGW tmpnam implementation is forwarded to the CRT directly.
- -- https://sourceforge.net/p/mingw-w64/discussion/723797/thread/55520785/
- -- MinGW links to an older CRT release (msvcrt.dll).
- -- Due to legal concerns MinGW will never use a newer CRT.
- --
- -- Make use of TEMP to compose the temporary filename if an old
- -- style tmpnam return value is detected.
- if filetocheck:sub(1, 1) == "\\" then
- local tempfolder = os.getenv("TEMP")
- return tempfolder .. filetocheck
- end
+ local temp = core.get_temp_path()
+ return temp .. DIR_DELIM .. "MT_" .. math.random(0, 10000)
+end
- local randname = "MTTempModFolder_" .. math.random(0,10000)
- local backstring = filetocheck:reverse()
- return filetocheck:sub(0, filetocheck:len() - backstring:find(DIR_DELIM) + 1) ..
- randname
+--------------------------------------------------------------------------------
+os.tmpname = function()
+ local path = os.tempfolder()
+ io.open(path, "w"):close()
+ return path
end
--------------------------------------------------------------------------------
@@ -227,41 +189,6 @@ function menu_handle_key_up_down(fields, textlist, settingname)
end
--------------------------------------------------------------------------------
-function asyncOnlineFavourites()
- if not menudata.public_known then
- menudata.public_known = {{
- name = fgettext("Loading..."),
- description = fgettext_ne("Try reenabling public serverlist and check your internet connection.")
- }}
- end
- menudata.favorites = menudata.public_known
- menudata.favorites_is_public = true
-
- if not menudata.public_downloading then
- menudata.public_downloading = true
- else
- return
- end
-
- core.handle_async(
- function(param)
- return core.get_favorites("online")
- end,
- nil,
- function(result)
- menudata.public_downloading = nil
- local favs = order_favorite_list(result)
- if favs[1] then
- menudata.public_known = favs
- menudata.favorites = menudata.public_known
- menudata.favorites_is_public = true
- end
- core.event_handler("Refresh")
- end
- )
-end
-
---------------------------------------------------------------------------------
function text2textlist(xpos, ypos, width, height, tl_name, textlen, text, transparency)
local textlines = core.wrap_text(text, textlen, true)
local retval = "textlist[" .. xpos .. "," .. ypos .. ";" .. width ..
diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua
index 2cf70c9c9..9bdf92a74 100644
--- a/builtin/mainmenu/dlg_config_world.lua
+++ b/builtin/mainmenu/dlg_config_world.lua
@@ -74,7 +74,7 @@ local function get_formspec(data)
"label[1.75,0;" .. data.worldspec.name .. "]"
if mod.is_modpack or mod.type == "game" then
- local info = minetest.formspec_escape(
+ local info = core.formspec_escape(
core.get_content_info(mod.path).description)
if info == "" then
if mod.is_modpack then
diff --git a/builtin/mainmenu/dlg_contentstore.lua b/builtin/mainmenu/dlg_contentstore.lua
index 01c42be0b..b0736a4fd 100644
--- a/builtin/mainmenu/dlg_contentstore.lua
+++ b/builtin/mainmenu/dlg_contentstore.lua
@@ -15,7 +15,7 @@
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-if not minetest.get_http_api then
+if not core.get_http_api then
function create_store_dlg()
return messagebox("store",
fgettext("ContentDB is not available when Minetest was compiled without cURL"))
@@ -23,9 +23,11 @@ if not minetest.get_http_api then
return
end
-local store = { packages = {}, packages_full = {} }
+-- Unordered preserves the original order of the ContentDB API,
+-- before the package list is ordered based on installed state.
+local store = { packages = {}, packages_full = {}, packages_full_unordered = {} }
-local http = minetest.get_http_api()
+local http = core.get_http_api()
-- Screenshot
local screenshot_dir = core.get_cache_path() .. DIR_DELIM .. "cdb"
@@ -45,6 +47,9 @@ local filter_types_titles = {
fgettext("Texture packs"),
}
+local number_downloading = 0
+local download_queue = {}
+
local filter_types_type = {
nil,
"game",
@@ -67,12 +72,14 @@ local function download_package(param)
end
end
-local function start_install(calling_dialog, package)
+local function start_install(package)
local params = {
package = package,
filename = os.tempfolder() .. "_MODNAME_" .. package.name .. ".zip",
}
+ number_downloading = number_downloading + 1
+
local function callback(result)
if result.successful then
local path, msg = pkgmgr.install(package.type,
@@ -121,9 +128,20 @@ local function start_install(calling_dialog, package)
end
package.downloading = false
+
+ number_downloading = number_downloading - 1
+
+ local next = download_queue[1]
+ if next then
+ table.remove(download_queue, 1)
+
+ start_install(next)
+ end
+
ui.update()
end
+ package.queued = false
package.downloading = true
if not core.handle_async(download_package, params, callback) then
@@ -133,6 +151,341 @@ local function start_install(calling_dialog, package)
end
end
+local function queue_download(package)
+ local max_concurrent_downloads = tonumber(core.settings:get("contentdb_max_concurrent_downloads"))
+ if number_downloading < max_concurrent_downloads then
+ start_install(package)
+ else
+ table.insert(download_queue, package)
+ package.queued = true
+ end
+end
+
+local function get_raw_dependencies(package)
+ if package.raw_deps then
+ return package.raw_deps
+ end
+
+ local url_fmt = "/api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s"
+ local version = core.get_version()
+ local base_url = core.settings:get("contentdb_url")
+ local url = base_url .. url_fmt:format(package.id, core.get_max_supp_proto(), version.string)
+
+ local response = http.fetch_sync({ url = url })
+ if not response.succeeded then
+ return
+ end
+
+ local data = core.parse_json(response.data) or {}
+
+ local content_lookup = {}
+ for _, pkg in pairs(store.packages_full) do
+ content_lookup[pkg.id] = pkg
+ end
+
+ for id, raw_deps in pairs(data) do
+ local package2 = content_lookup[id:lower()]
+ if package2 and not package2.raw_deps then
+ package2.raw_deps = raw_deps
+
+ for _, dep in pairs(raw_deps) do
+ local packages = {}
+ for i=1, #dep.packages do
+ packages[#packages + 1] = content_lookup[dep.packages[i]:lower()]
+ end
+ dep.packages = packages
+ end
+ end
+ end
+
+ return package.raw_deps
+end
+
+local function has_hard_deps(raw_deps)
+ for i=1, #raw_deps do
+ if not raw_deps[i].is_optional then
+ return true
+ end
+ end
+
+ return false
+end
+
+-- Recursively resolve dependencies, given the installed mods
+local function resolve_dependencies_2(raw_deps, installed_mods, out)
+ local function resolve_dep(dep)
+ -- Check whether it's already installed
+ if installed_mods[dep.name] then
+ return {
+ is_optional = dep.is_optional,
+ name = dep.name,
+ installed = true,
+ }
+ end
+
+ -- Find exact name matches
+ local fallback
+ for _, package in pairs(dep.packages) do
+ if package.type ~= "game" then
+ if package.name == dep.name then
+ return {
+ is_optional = dep.is_optional,
+ name = dep.name,
+ installed = false,
+ package = package,
+ }
+ elseif not fallback then
+ fallback = package
+ end
+ end
+ end
+
+ -- Otherwise, find the first mod that fulfils it
+ if fallback then
+ return {
+ is_optional = dep.is_optional,
+ name = dep.name,
+ installed = false,
+ package = fallback,
+ }
+ end
+
+ return {
+ is_optional = dep.is_optional,
+ name = dep.name,
+ installed = false,
+ }
+ end
+
+ for _, dep in pairs(raw_deps) do
+ if not dep.is_optional and not out[dep.name] then
+ local result = resolve_dep(dep)
+ out[dep.name] = result
+ if result and result.package and not result.installed then
+ local raw_deps2 = get_raw_dependencies(result.package)
+ if raw_deps2 then
+ resolve_dependencies_2(raw_deps2, installed_mods, out)
+ end
+ end
+ end
+ end
+
+ return true
+end
+
+-- Resolve dependencies for a package, calls the recursive version.
+local function resolve_dependencies(raw_deps, game)
+ assert(game)
+
+ local installed_mods = {}
+
+ local mods = {}
+ pkgmgr.get_game_mods(game, mods)
+ for _, mod in pairs(mods) do
+ installed_mods[mod.name] = true
+ end
+
+ for _, mod in pairs(pkgmgr.global_mods:get_list()) do
+ installed_mods[mod.name] = true
+ end
+
+ local out = {}
+ if not resolve_dependencies_2(raw_deps, installed_mods, out) then
+ return nil
+ end
+
+ local retval = {}
+ for _, dep in pairs(out) do
+ retval[#retval + 1] = dep
+ end
+
+ table.sort(retval, function(a, b)
+ return a.name < b.name
+ end)
+
+ return retval
+end
+
+local install_dialog = {}
+function install_dialog.get_formspec()
+ local package = install_dialog.package
+ local raw_deps = install_dialog.raw_deps
+ local will_install_deps = install_dialog.will_install_deps
+
+ local selected_game_idx = 1
+ local selected_gameid = core.settings:get("menu_last_game")
+ local games = table.copy(pkgmgr.games)
+ for i=1, #games do
+ if selected_gameid and games[i].id == selected_gameid then
+ selected_game_idx = i
+ end
+
+ games[i] = core.formspec_escape(games[i].name)
+ end
+
+ local selected_game = pkgmgr.games[selected_game_idx]
+ local deps_to_install = 0
+ local deps_not_found = 0
+
+ install_dialog.dependencies = resolve_dependencies(raw_deps, selected_game)
+ local formatted_deps = {}
+ for _, dep in pairs(install_dialog.dependencies) do
+ formatted_deps[#formatted_deps + 1] = "#fff"
+ formatted_deps[#formatted_deps + 1] = core.formspec_escape(dep.name)
+ if dep.installed then
+ formatted_deps[#formatted_deps + 1] = "#ccf"
+ formatted_deps[#formatted_deps + 1] = fgettext("Already installed")
+ elseif dep.package then
+ formatted_deps[#formatted_deps + 1] = "#cfc"
+ formatted_deps[#formatted_deps + 1] = fgettext("$1 by $2", dep.package.title, dep.package.author)
+ deps_to_install = deps_to_install + 1
+ else
+ formatted_deps[#formatted_deps + 1] = "#f00"
+ formatted_deps[#formatted_deps + 1] = fgettext("Not found")
+ deps_not_found = deps_not_found + 1
+ end
+ end
+
+ local message_bg = "#3333"
+ local message
+ if will_install_deps then
+ message = fgettext("$1 and $2 dependencies will be installed.", package.title, deps_to_install)
+ else
+ message = fgettext("$1 will be installed, and $2 dependencies will be skipped.", package.title, deps_to_install)
+ end
+ if deps_not_found > 0 then
+ message = fgettext("$1 required dependencies could not be found.", deps_not_found) ..
+ " " .. fgettext("Please check that the base game is correct.", deps_not_found) ..
+ "\n" .. message
+ message_bg = mt_color_orange
+ end
+
+ local formspec = {
+ "formspec_version[3]",
+ "size[7,7.85]",
+ "style[title;border=false]",
+ "box[0,0;7,0.5;#3333]",
+ "button[0,0;7,0.5;title;", fgettext("Install $1", package.title) , "]",
+
+ "container[0.375,0.70]",
+
+ "label[0,0.25;", fgettext("Base Game:"), "]",
+ "dropdown[2,0;4.25,0.5;gameid;", table.concat(games, ","), ";", selected_game_idx, "]",
+
+ "label[0,0.8;", fgettext("Dependencies:"), "]",
+
+ "tablecolumns[color;text;color;text]",
+ "table[0,1.1;6.25,3;packages;", table.concat(formatted_deps, ","), "]",
+
+ "container_end[]",
+
+ "checkbox[0.375,5.1;will_install_deps;",
+ fgettext("Install missing dependencies"), ";",
+ will_install_deps and "true" or "false", "]",
+
+ "box[0,5.4;7,1.2;", message_bg, "]",
+ "textarea[0.375,5.5;6.25,1;;;", message, "]",
+
+ "container[1.375,6.85]",
+ "button[0,0;2,0.8;install_all;", fgettext("Install"), "]",
+ "button[2.25,0;2,0.8;cancel;", fgettext("Cancel"), "]",
+ "container_end[]",
+ }
+
+ return table.concat(formspec, "")
+end
+
+function install_dialog.handle_submit(this, fields)
+ if fields.cancel then
+ this:delete()
+ return true
+ end
+
+ if fields.will_install_deps ~= nil then
+ install_dialog.will_install_deps = core.is_yes(fields.will_install_deps)
+ return true
+ end
+
+ if fields.install_all then
+ queue_download(install_dialog.package)
+
+ if install_dialog.will_install_deps then
+ for _, dep in pairs(install_dialog.dependencies) do
+ if not dep.is_optional and not dep.installed and dep.package then
+ queue_download(dep.package)
+ end
+ end
+ end
+
+ this:delete()
+ return true
+ end
+
+ if fields.gameid then
+ for _, game in pairs(pkgmgr.games) do
+ if game.name == fields.gameid then
+ core.settings:set("menu_last_game", game.id)
+ break
+ end
+ end
+ return true
+ end
+
+ return false
+end
+
+function install_dialog.create(package, raw_deps)
+ install_dialog.dependencies = nil
+ install_dialog.package = package
+ install_dialog.raw_deps = raw_deps
+ install_dialog.will_install_deps = true
+ return dialog_create("install_dialog",
+ install_dialog.get_formspec,
+ install_dialog.handle_submit,
+ nil)
+end
+
+
+local confirm_overwrite = {}
+function confirm_overwrite.get_formspec()
+ local package = confirm_overwrite.package
+
+ return "size[11.5,4.5,true]" ..
+ "label[2,2;" ..
+ fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name) .. "]"..
+ "style[install;bgcolor=red]" ..
+ "button[3.25,3.5;2.5,0.5;install;" .. fgettext("Overwrite") .. "]" ..
+ "button[5.75,3.5;2.5,0.5;cancel;" .. fgettext("Cancel") .. "]"
+end
+
+function confirm_overwrite.handle_submit(this, fields)
+ if fields.cancel then
+ this:delete()
+ return true
+ end
+
+ if fields.install then
+ this:delete()
+ confirm_overwrite.callback()
+ return true
+ end
+
+ return false
+end
+
+function confirm_overwrite.create(package, callback)
+ assert(type(package) == "table")
+ assert(type(callback) == "function")
+
+ confirm_overwrite.package = package
+ confirm_overwrite.callback = callback
+ return dialog_create("confirm_overwrite",
+ confirm_overwrite.get_formspec,
+ confirm_overwrite.handle_submit,
+ nil)
+end
+
+
local function get_file_extension(path)
local parts = path:split(".")
return parts[#parts]
@@ -200,7 +553,7 @@ function store.load()
end
end
- local timeout = tonumber(minetest.settings:get("curl_file_download_timeout"))
+ local timeout = tonumber(core.settings:get("curl_file_download_timeout"))
local response = http.fetch_sync({ url = url, timeout = timeout })
if not response.succeeded then
return
@@ -221,6 +574,7 @@ function store.load()
end
end
+ store.packages_full_unordered = store.packages_full
store.packages = store.packages_full
store.loaded = true
end
@@ -229,7 +583,7 @@ function store.update_paths()
local mod_hash = {}
pkgmgr.refresh_globals()
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
- if mod.author then
+ if mod.author and mod.release > 0 then
mod_hash[mod.author:lower() .. "/" .. mod.name] = mod
end
end
@@ -237,14 +591,14 @@ function store.update_paths()
local game_hash = {}
pkgmgr.update_gamelist()
for _, game in pairs(pkgmgr.games) do
- if game.author ~= "" then
+ if game.author ~= "" and game.release > 0 then
game_hash[game.author:lower() .. "/" .. game.id] = game
end
end
local txp_hash = {}
for _, txp in pairs(pkgmgr.get_texture_packs()) do
- if txp.author then
+ if txp.author and txp.release > 0 then
txp_hash[txp.author:lower() .. "/" .. txp.name] = txp
end
end
@@ -268,6 +622,33 @@ function store.update_paths()
end
end
+function store.sort_packages()
+ local ret = {}
+
+ -- Add installed content
+ for i=1, #store.packages_full_unordered do
+ local package = store.packages_full_unordered[i]
+ if package.path then
+ ret[#ret + 1] = package
+ end
+ end
+
+ -- Sort installed content by title
+ table.sort(ret, function(a, b)
+ return a.title < b.title
+ end)
+
+ -- Add uninstalled content
+ for i=1, #store.packages_full_unordered do
+ local package = store.packages_full_unordered[i]
+ if not package.path then
+ ret[#ret + 1] = package
+ end
+ end
+
+ store.packages_full = ret
+end
+
function store.filter_packages(query)
if query == "" and filter_type == 1 then
store.packages = store.packages_full
@@ -279,7 +660,7 @@ function store.filter_packages(query)
table.insert(keywords, word)
end
- local function matches_keywords(package, keywords)
+ local function matches_keywords(package)
for k = 1, #keywords do
local keyword = keywords[k]
@@ -296,12 +677,11 @@ function store.filter_packages(query)
store.packages = {}
for _, package in pairs(store.packages_full) do
- if (query == "" or matches_keywords(package, keywords)) and
+ if (query == "" or matches_keywords(package)) and
(filter_type == 1 or package.type == filter_types_type[filter_type]) then
store.packages[#store.packages + 1] = package
end
end
-
end
function store.get_formspec(dlgdata)
@@ -314,18 +694,21 @@ function store.get_formspec(dlgdata)
local W = 15.75
local H = 9.5
-
local formspec
if #store.packages_full > 0 then
formspec = {
"formspec_version[3]",
"size[15.75,9.5]",
"position[0.5,0.55]",
+
+ "style[status,downloading,queued;border=false]",
+
"container[0.375,0.375]",
- "field[0,0;10.225,0.8;search_string;;", core.formspec_escape(search_string), "]",
+ "field[0,0;7.225,0.8;search_string;;", core.formspec_escape(search_string), "]",
"field_close_on_enter[search_string;false]",
- "button[10.225,0;2,0.8;search;", fgettext("Search"), "]",
- "dropdown[12.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]",
+ "image_button[7.3,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
+ "image_button[8.125,0;0.8,0.8;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";clear;]",
+ "dropdown[9.6,0;2.4,0.8;type;", table.concat(filter_types_titles, ","), ";", filter_type, "]",
"container_end[]",
-- Page nav buttons
@@ -344,6 +727,35 @@ function store.get_formspec(dlgdata)
"container_end[]",
}
+ if number_downloading > 0 then
+ formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;downloading;"
+ if #download_queue > 0 then
+ formspec[#formspec + 1] = fgettext("$1 downloading,\n$2 queued", number_downloading, #download_queue)
+ else
+ formspec[#formspec + 1] = fgettext("$1 downloading...", number_downloading)
+ end
+ formspec[#formspec + 1] = "]"
+ else
+ local num_avail_updates = 0
+ for i=1, #store.packages_full do
+ local package = store.packages_full[i]
+ if package.path and package.installed_release < package.release and
+ not (package.downloading or package.queued) then
+ num_avail_updates = num_avail_updates + 1
+ end
+ end
+
+ if num_avail_updates == 0 then
+ formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;status;"
+ formspec[#formspec + 1] = fgettext("No updates")
+ formspec[#formspec + 1] = "]"
+ else
+ formspec[#formspec + 1] = "button[12.75,0.375;2.625,0.8;update_all;"
+ formspec[#formspec + 1] = fgettext("Update All [$1]", num_avail_updates)
+ formspec[#formspec + 1] = "]"
+ end
+ end
+
if #store.packages == 0 then
formspec[#formspec + 1] = "label[4,3;"
formspec[#formspec + 1] = fgettext("No results")
@@ -360,11 +772,17 @@ function store.get_formspec(dlgdata)
}
end
+ -- download/queued tooltips always have the same message
+ local tooltip_colors = ";#dff6f5;#302c2e]"
+ formspec[#formspec + 1] = "tooltip[downloading;" .. fgettext("Downloading...") .. tooltip_colors
+ formspec[#formspec + 1] = "tooltip[queued;" .. fgettext("Queued") .. tooltip_colors
+
local start_idx = (cur_page - 1) * num_per_page + 1
for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do
local package = store.packages[i]
+ local container_y = (i - start_idx) * 1.375 + (2*0.375 + 0.8)
formspec[#formspec + 1] = "container[0.375,"
- formspec[#formspec + 1] = (i - start_idx) * 1.375 + (2*0.375 + 0.8)
+ formspec[#formspec + 1] = container_y
formspec[#formspec + 1] = "]"
-- image
@@ -375,55 +793,55 @@ function store.get_formspec(dlgdata)
-- title
formspec[#formspec + 1] = "label[1.875,0.1;"
formspec[#formspec + 1] = core.formspec_escape(
- minetest.colorize(mt_color_green, package.title) ..
- minetest.colorize("#BFBFBF", " by " .. package.author))
+ core.colorize(mt_color_green, package.title) ..
+ core.colorize("#BFBFBF", " by " .. package.author))
formspec[#formspec + 1] = "]"
-- buttons
- local description_width = W - 0.375*5 - 1 - 2*1.5
+ local left_base = "image_button[-1.55,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
formspec[#formspec + 1] = "container["
formspec[#formspec + 1] = W - 0.375*2
formspec[#formspec + 1] = ",0.1]"
if package.downloading then
- formspec[#formspec + 1] = "style[download;border=false]"
-
- formspec[#formspec + 1] = "button[-3.5,0;2,0.8;download;"
- formspec[#formspec + 1] = fgettext("Downloading...")
- formspec[#formspec + 1] = "]"
+ formspec[#formspec + 1] = "animated_image[-1.7,-0.15;1,1;downloading;"
+ formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
+ formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
+ elseif package.queued then
+ formspec[#formspec + 1] = left_base
+ formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
+ formspec[#formspec + 1] = "cdb_queued.png;queued]"
elseif not package.path then
- formspec[#formspec + 1] = "button[-3,0;1.5,0.8;install_"
- formspec[#formspec + 1] = tostring(i)
- formspec[#formspec + 1] = ";"
- formspec[#formspec + 1] = fgettext("Install")
- formspec[#formspec + 1] = "]"
+ local elem_name = "install_" .. i .. ";"
+ formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]"
+ formspec[#formspec + 1] = left_base .. "cdb_add.png;" .. elem_name .. "]"
+ formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Install") .. tooltip_colors
else
if package.installed_release < package.release then
- description_width = description_width - 1.5
-- The install_ action also handles updating
- formspec[#formspec + 1] = "button[-4.5,0;1.5,0.8;install_"
- formspec[#formspec + 1] = tostring(i)
- formspec[#formspec + 1] = ";"
- formspec[#formspec + 1] = fgettext("Update")
- formspec[#formspec + 1] = "]"
- end
+ local elem_name = "install_" .. i .. ";"
+ formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#28ccdf]"
+ formspec[#formspec + 1] = left_base .. "cdb_update.png;" .. elem_name .. "]"
+ formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Update") .. tooltip_colors
+ else
- formspec[#formspec + 1] = "button[-3,0;1.5,0.8;uninstall_"
- formspec[#formspec + 1] = tostring(i)
- formspec[#formspec + 1] = ";"
- formspec[#formspec + 1] = fgettext("Uninstall")
- formspec[#formspec + 1] = "]"
+ local elem_name = "uninstall_" .. i .. ";"
+ formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]"
+ formspec[#formspec + 1] = left_base .. "cdb_clear.png;" .. elem_name .. "]"
+ formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors
+ end
end
- formspec[#formspec + 1] = "button[-1.5,0;1.5,0.8;view_"
- formspec[#formspec + 1] = tostring(i)
- formspec[#formspec + 1] = ";"
- formspec[#formspec + 1] = fgettext("View")
- formspec[#formspec + 1] = "]"
+ local web_elem_name = "view_" .. i .. ";"
+ formspec[#formspec + 1] = "image_button[-0.7,0;0.7,0.7;" ..
+ core.formspec_escape(defaulttexturedir) .. "cdb_viewonline.png;" .. web_elem_name .. "]"
+ formspec[#formspec + 1] = "tooltip[" .. web_elem_name ..
+ fgettext("View more information in a web browser") .. tooltip_colors
formspec[#formspec + 1] = "container_end[]"
-- description
+ local description_width = W - 0.375*5 - 0.85 - 2*0.7
formspec[#formspec + 1] = "textarea[1.855,0.3;"
formspec[#formspec + 1] = tostring(description_width)
formspec[#formspec + 1] = ",0.8;;;"
@@ -444,6 +862,13 @@ function store.handle_submit(this, fields)
return true
end
+ if fields.clear then
+ search_string = ""
+ cur_page = 1
+ store.filter_packages("")
+ return true
+ end
+
if fields.back then
this:delete()
return true
@@ -485,6 +910,17 @@ function store.handle_submit(this, fields)
end
end
+ if fields.update_all then
+ for i=1, #store.packages_full do
+ local package = store.packages_full[i]
+ if package.path and package.installed_release < package.release and
+ not (package.downloading or package.queued) then
+ queue_download(package)
+ end
+ end
+ return true
+ end
+
local start_idx = (cur_page - 1) * num_per_page + 1
assert(start_idx ~= nil)
for i=start_idx, math.min(#store.packages, start_idx+num_per_page-1) do
@@ -492,21 +928,54 @@ function store.handle_submit(this, fields)
assert(package)
if fields["install_" .. i] then
- start_install(this, package)
+ local install_parent
+ if package.type == "mod" then
+ install_parent = core.get_modpath()
+ elseif package.type == "game" then
+ install_parent = core.get_gamepath()
+ elseif package.type == "txp" then
+ install_parent = core.get_texturepath()
+ else
+ error("Unknown package type: " .. package.type)
+ end
+
+
+ local function on_confirm()
+ local deps = get_raw_dependencies(package)
+ if deps and has_hard_deps(deps) then
+ local dlg = install_dialog.create(package, deps)
+ dlg:set_parent(this)
+ this:hide()
+ dlg:show()
+ else
+ queue_download(package)
+ end
+ end
+
+ if not package.path and core.is_dir(install_parent .. DIR_DELIM .. package.name) then
+ local dlg = confirm_overwrite.create(package, on_confirm)
+ dlg:set_parent(this)
+ this:hide()
+ dlg:show()
+ else
+ on_confirm()
+ end
+
return true
end
if fields["uninstall_" .. i] then
- local dlg_delmod = create_delete_content_dlg(package)
- dlg_delmod:set_parent(this)
+ local dlg = create_delete_content_dlg(package)
+ dlg:set_parent(this)
this:hide()
- dlg_delmod:show()
+ dlg:show()
return true
end
if fields["view_" .. i] then
- local url = ("%s/packages/%s?protocol_version=%d"):format(
- core.settings:get("contentdb_url"), package.id, core.get_max_supp_proto())
+ local url = ("%s/packages/%s/%s?protocol_version=%d"):format(
+ core.settings:get("contentdb_url"),
+ package.author, package.name, core.get_max_supp_proto())
core.open_url(url)
return true
end
@@ -520,6 +989,9 @@ function create_store_dlg(type)
store.load()
end
+ store.update_paths()
+ store.sort_packages()
+
search_string = ""
cur_page = 1
diff --git a/builtin/mainmenu/dlg_create_world.lua b/builtin/mainmenu/dlg_create_world.lua
index 36df23cce..1938747fe 100644
--- a/builtin/mainmenu/dlg_create_world.lua
+++ b/builtin/mainmenu/dlg_create_world.lua
@@ -61,6 +61,7 @@ local flag_checkboxes = {
fgettext("Low humidity and high heat causes shallow or dry rivers") },
},
flat = {
+ cb_caverns,
{ "hills", fgettext("Hills"), "hills" },
{ "lakes", fgettext("Lakes"), "lakes" },
},
@@ -97,7 +98,7 @@ local function create_world_formspec(dialogdata)
-- Error out when no games found
if #pkgmgr.games == 0 then
return "size[12.25,3,true]" ..
- "box[0,0;12,2;#ff8800]" ..
+ "box[0,0;12,2;" .. mt_color_orange .. "]" ..
"textarea[0.3,0;11.7,2;;;"..
fgettext("You have no games installed.") .. "\n" ..
fgettext("Download one from minetest.net") .. "]" ..
@@ -362,10 +363,18 @@ local function create_world_buttonhandler(this, fields)
local gameindex = core.get_textlist_index("games")
if gameindex ~= nil then
+ -- For unnamed worlds use the generated name 'world<number>',
+ -- where the number increments: it is set to 1 larger than the largest
+ -- generated name number found.
if worldname == "" then
- local random_number = math.random(10000, 99999)
- local random_world_name = "Unnamed" .. random_number
- worldname = random_world_name
+ local worldnum_max = 0
+ for _, world in ipairs(menudata.worldlist:get_list()) do
+ if world.name:match("^world%d+$") then
+ local worldnum = tonumber(world.name:sub(6))
+ worldnum_max = math.max(worldnum_max, worldnum)
+ end
+ end
+ worldname = "world" .. worldnum_max + 1
end
core.settings:set("fixed_map_seed", fields["te_seed"])
@@ -434,7 +443,7 @@ local function create_world_buttonhandler(this, fields)
end
if fields["mgv6_biomes"] then
- local entry = minetest.formspec_escape(fields["mgv6_biomes"])
+ local entry = core.formspec_escape(fields["mgv6_biomes"])
for b=1, #mgv6_biomes do
if entry == mgv6_biomes[b][1] then
local ftable = core.settings:get_flags("mgv6_spflags")
diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua
index c17e79270..45089c7c9 100644
--- a/builtin/mainmenu/init.lua
+++ b/builtin/mainmenu/init.lua
@@ -19,10 +19,10 @@ mt_color_grey = "#AAAAAA"
mt_color_blue = "#6389FF"
mt_color_green = "#72FF63"
mt_color_dark_green = "#25C191"
+mt_color_orange = "#FF8800"
local menupath = core.get_mainmenu_path()
local basepath = core.get_builtin_path()
-local menustyle = core.settings:get("main_menu_style")
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
DIR_DELIM .. "pack" .. DIR_DELIM
@@ -34,29 +34,24 @@ dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua")
dofile(menupath .. DIR_DELIM .. "async_event.lua")
dofile(menupath .. DIR_DELIM .. "common.lua")
dofile(menupath .. DIR_DELIM .. "pkgmgr.lua")
+dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
dofile(menupath .. DIR_DELIM .. "textures.lua")
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua")
dofile(menupath .. DIR_DELIM .. "dlg_contentstore.lua")
-if menustyle ~= "simple" then
- dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
- dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
- dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
- dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
-end
+dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
+dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
+dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
+dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
local tabs = {}
tabs.settings = dofile(menupath .. DIR_DELIM .. "tab_settings.lua")
tabs.content = dofile(menupath .. DIR_DELIM .. "tab_content.lua")
tabs.credits = dofile(menupath .. DIR_DELIM .. "tab_credits.lua")
-if menustyle == "simple" then
- tabs.simple_main = dofile(menupath .. DIR_DELIM .. "tab_simple_main.lua")
-else
- tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua")
- tabs.play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua")
-end
+tabs.local_game = dofile(menupath .. DIR_DELIM .. "tab_local.lua")
+tabs.play_online = dofile(menupath .. DIR_DELIM .. "tab_online.lua")
--------------------------------------------------------------------------------
local function main_event_handler(tabview, event)
@@ -71,68 +66,35 @@ local function init_globals()
-- Init gamedata
gamedata.worldindex = 0
- if menustyle == "simple" then
- local world_list = core.get_worlds()
- local world_index
-
- local found_singleplayerworld = false
- for i, world in ipairs(world_list) do
- if world.name == "singleplayerworld" then
- found_singleplayerworld = true
- world_index = i
- break
- end
- end
-
- if not found_singleplayerworld then
- core.create_world("singleplayerworld", 1)
-
- world_list = core.get_worlds()
-
- for i, world in ipairs(world_list) do
- if world.name == "singleplayerworld" then
- world_index = i
- break
- end
- end
+ menudata.worldlist = filterlist.create(
+ core.get_worlds,
+ compare_worlds,
+ -- Unique id comparison function
+ function(element, uid)
+ return element.name == uid
+ end,
+ -- Filter function
+ function(element, gameid)
+ return element.gameid == gameid
end
+ )
- gamedata.worldindex = world_index
- else
- menudata.worldlist = filterlist.create(
- core.get_worlds,
- compare_worlds,
- -- Unique id comparison function
- function(element, uid)
- return element.name == uid
- end,
- -- Filter function
- function(element, gameid)
- return element.gameid == gameid
- end
- )
-
- menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic)
- menudata.worldlist:set_sortmode("alphabetic")
-
- if not core.settings:get("menu_last_game") then
- local default_game = core.settings:get("default_game") or "minetest"
- core.settings:set("menu_last_game", default_game)
- end
+ menudata.worldlist:add_sort_mechanism("alphabetic", sort_worlds_alphabetic)
+ menudata.worldlist:set_sortmode("alphabetic")
- mm_texture.init()
+ if not core.settings:get("menu_last_game") then
+ local default_game = core.settings:get("default_game") or "minetest"
+ core.settings:set("menu_last_game", default_game)
end
+ mm_texture.init()
+
-- Create main tabview
local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0})
- if menustyle == "simple" then
- tv_main:add(tabs.simple_main)
- else
- tv_main:set_autosave_tab(true)
- tv_main:add(tabs.local_game)
- tv_main:add(tabs.play_online)
- end
+ tv_main:set_autosave_tab(true)
+ tv_main:add(tabs.local_game)
+ tv_main:add(tabs.play_online)
tv_main:add(tabs.content)
tv_main:add(tabs.settings)
@@ -141,12 +103,20 @@ local function init_globals()
tv_main:set_global_event_handler(main_event_handler)
tv_main:set_fixed_size(false)
- if menustyle ~= "simple" then
- local last_tab = core.settings:get("maintab_LAST")
- if last_tab and tv_main.current_tab ~= last_tab then
- tv_main:set_tab(last_tab)
+ local last_tab = core.settings:get("maintab_LAST")
+ if last_tab and tv_main.current_tab ~= last_tab then
+ tv_main:set_tab(last_tab)
+ end
+
+ -- In case the folder of the last selected game has been deleted,
+ -- display "Minetest" as a header
+ if tv_main.current_tab == "local" then
+ local game = pkgmgr.find_by_gameid(core.settings:get("menu_last_game"))
+ if game == nil then
+ mm_texture.reset()
end
end
+
ui.set_default("maintab")
tv_main:show()
diff --git a/builtin/mainmenu/pkgmgr.lua b/builtin/mainmenu/pkgmgr.lua
index 5b8807310..19127d8d3 100644
--- a/builtin/mainmenu/pkgmgr.lua
+++ b/builtin/mainmenu/pkgmgr.lua
@@ -72,6 +72,34 @@ local function cleanup_path(temppath)
return temppath
end
+local function load_texture_packs(txtpath, retval)
+ local list = core.get_dir_list(txtpath, true)
+ local current_texture_path = core.settings:get("texture_path")
+
+ for _, item in ipairs(list) do
+ if item ~= "base" then
+ local name = item
+
+ local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM
+ if path == current_texture_path then
+ name = fgettext("$1 (Enabled)", name)
+ end
+
+ local conf = Settings(path .. "texture_pack.conf")
+
+ retval[#retval + 1] = {
+ name = item,
+ author = conf:get("author"),
+ release = tonumber(conf:get("release") or "0"),
+ list_name = name,
+ type = "txp",
+ path = path,
+ enabled = path == current_texture_path,
+ }
+ end
+ end
+end
+
function get_mods(path,retval,modpack)
local mods = core.get_dir_list(path, true)
@@ -112,7 +140,7 @@ function get_mods(path,retval,modpack)
toadd.type = "mod"
-- Check modpack.txt
- -- Note: modpack.conf is already checked above
+ -- Note: modpack.conf is already checked above
local modpackfile = io.open(prefix .. DIR_DELIM .. "modpack.txt")
if modpackfile then
modpackfile:close()
@@ -136,32 +164,13 @@ pkgmgr = {}
function pkgmgr.get_texture_packs()
local txtpath = core.get_texturepath()
- local list = core.get_dir_list(txtpath, true)
+ local txtpath_system = core.get_texturepath_share()
local retval = {}
- local current_texture_path = core.settings:get("texture_path")
-
- for _, item in ipairs(list) do
- if item ~= "base" then
- local name = item
-
- local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM
- if path == current_texture_path then
- name = fgettext("$1 (Enabled)", name)
- end
-
- local conf = Settings(path .. "texture_pack.conf")
-
- retval[#retval + 1] = {
- name = item,
- author = conf:get("author"),
- release = tonumber(conf:get("release") or "0"),
- list_name = name,
- type = "txp",
- path = path,
- enabled = path == current_texture_path,
- }
- end
+ load_texture_packs(txtpath, retval)
+ -- on portable versions these two paths coincide. It avoids loading the path twice
+ if txtpath ~= txtpath_system then
+ load_texture_packs(txtpath_system, retval)
end
table.sort(retval, function(a, b)
@@ -450,7 +459,7 @@ function pkgmgr.enable_mod(this, toset)
if not toset then
-- Mod(s) were disabled, so no dependencies need to be enabled
table.sort(toggled_mods)
- minetest.log("info", "Following mods were disabled: " ..
+ core.log("info", "Following mods were disabled: " ..
table.concat(toggled_mods, ", "))
return
end
@@ -487,7 +496,7 @@ function pkgmgr.enable_mod(this, toset)
enabled_mods[name] = true
local mod_to_enable = list[mod_ids[name]]
if not mod_to_enable then
- minetest.log("warning", "Mod dependency \"" .. name ..
+ core.log("warning", "Mod dependency \"" .. name ..
"\" not found!")
else
if mod_to_enable.enabled == false then
@@ -508,7 +517,7 @@ function pkgmgr.enable_mod(this, toset)
-- Log the list of enabled mods
table.sort(toggled_mods)
- minetest.log("info", "Following mods were enabled: " ..
+ core.log("info", "Following mods were enabled: " ..
table.concat(toggled_mods, ", "))
end
diff --git a/builtin/mainmenu/serverlistmgr.lua b/builtin/mainmenu/serverlistmgr.lua
new file mode 100644
index 000000000..9876d8ac5
--- /dev/null
+++ b/builtin/mainmenu/serverlistmgr.lua
@@ -0,0 +1,250 @@
+--Minetest
+--Copyright (C) 2020 rubenwardy
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+serverlistmgr = {}
+
+--------------------------------------------------------------------------------
+local function order_server_list(list)
+ local res = {}
+ --orders the favorite list after support
+ for i = 1, #list do
+ local fav = list[i]
+ if is_server_protocol_compat(fav.proto_min, fav.proto_max) then
+ res[#res + 1] = fav
+ end
+ end
+ for i = 1, #list do
+ local fav = list[i]
+ if not is_server_protocol_compat(fav.proto_min, fav.proto_max) then
+ res[#res + 1] = fav
+ end
+ end
+ return res
+end
+
+local public_downloading = false
+
+--------------------------------------------------------------------------------
+function serverlistmgr.sync()
+ if not serverlistmgr.servers then
+ serverlistmgr.servers = {{
+ name = fgettext("Loading..."),
+ description = fgettext_ne("Try reenabling public serverlist and check your internet connection.")
+ }}
+ end
+
+ local serverlist_url = core.settings:get("serverlist_url") or ""
+ if not core.get_http_api or serverlist_url == "" then
+ serverlistmgr.servers = {{
+ name = fgettext("Public server list is disabled"),
+ description = ""
+ }}
+ return
+ end
+
+ if public_downloading then
+ return
+ end
+ public_downloading = true
+
+ core.handle_async(
+ function(param)
+ local http = core.get_http_api()
+ local url = ("%s/list?proto_version_min=%d&proto_version_max=%d"):format(
+ core.settings:get("serverlist_url"),
+ core.get_min_supp_proto(),
+ core.get_max_supp_proto())
+
+ local response = http.fetch_sync({ url = url })
+ if not response.succeeded then
+ return {}
+ end
+
+ local retval = core.parse_json(response.data)
+ return retval and retval.list or {}
+ end,
+ nil,
+ function(result)
+ public_downloading = nil
+ local favs = order_server_list(result)
+ if favs[1] then
+ serverlistmgr.servers = favs
+ end
+ core.event_handler("Refresh")
+ end
+ )
+end
+
+--------------------------------------------------------------------------------
+local function get_favorites_path()
+ local base = core.get_user_path() .. DIR_DELIM .. "client" .. DIR_DELIM .. "serverlist" .. DIR_DELIM
+ return base .. core.settings:get("serverlist_file")
+end
+
+--------------------------------------------------------------------------------
+local function save_favorites(favorites)
+ local filename = core.settings:get("serverlist_file")
+ -- If setting specifies legacy format change the filename to the new one
+ if filename:sub(#filename - 3):lower() == ".txt" then
+ core.settings:set("serverlist_file", filename:sub(1, #filename - 4) .. ".json")
+ end
+
+ local path = get_favorites_path()
+ core.create_dir(path)
+ core.safe_file_write(path, core.write_json(favorites))
+end
+
+--------------------------------------------------------------------------------
+function serverlistmgr.read_legacy_favorites(path)
+ local file = io.open(path, "r")
+ if not file then
+ return nil
+ end
+
+ local lines = {}
+ for line in file:lines() do
+ lines[#lines + 1] = line
+ end
+ file:close()
+
+ local favorites = {}
+
+ local i = 1
+ while i < #lines do
+ local function pop()
+ local line = lines[i]
+ i = i + 1
+ return line and line:trim()
+ end
+
+ if pop():lower() == "[server]" then
+ local name = pop()
+ local address = pop()
+ local port = tonumber(pop())
+ local description = pop()
+
+ if name == "" then
+ name = nil
+ end
+
+ if description == "" then
+ description = nil
+ end
+
+ if not address or #address < 3 then
+ core.log("warning", "Malformed favorites file, missing address at line " .. i)
+ elseif not port or port < 1 or port > 65535 then
+ core.log("warning", "Malformed favorites file, missing port at line " .. i)
+ elseif (name and name:upper() == "[SERVER]") or
+ (address and address:upper() == "[SERVER]") or
+ (description and description:upper() == "[SERVER]") then
+ core.log("warning", "Potentially malformed favorites file, overran at line " .. i)
+ else
+ favorites[#favorites + 1] = {
+ name = name,
+ address = address,
+ port = port,
+ description = description
+ }
+ end
+ end
+ end
+
+ return favorites
+end
+
+--------------------------------------------------------------------------------
+local function read_favorites()
+ local path = get_favorites_path()
+
+ -- If new format configured fall back to reading the legacy file
+ if path:sub(#path - 4):lower() == ".json" then
+ local file = io.open(path, "r")
+ if file then
+ local json = file:read("*all")
+ file:close()
+ return core.parse_json(json)
+ end
+
+ path = path:sub(1, #path - 5) .. ".txt"
+ end
+
+ local favs = serverlistmgr.read_legacy_favorites(path)
+ if favs then
+ save_favorites(favs)
+ os.remove(path)
+ end
+ return favs
+end
+
+--------------------------------------------------------------------------------
+local function delete_favorite(favorites, del_favorite)
+ for i=1, #favorites do
+ local fav = favorites[i]
+
+ if fav.address == del_favorite.address and fav.port == del_favorite.port then
+ table.remove(favorites, i)
+ return
+ end
+ end
+end
+
+--------------------------------------------------------------------------------
+function serverlistmgr.get_favorites()
+ if serverlistmgr.favorites then
+ return serverlistmgr.favorites
+ end
+
+ serverlistmgr.favorites = {}
+
+ -- Add favorites, removing duplicates
+ local seen = {}
+ for _, fav in ipairs(read_favorites() or {}) do
+ local key = ("%s:%d"):format(fav.address:lower(), fav.port)
+ if not seen[key] then
+ seen[key] = true
+ serverlistmgr.favorites[#serverlistmgr.favorites + 1] = fav
+ end
+ end
+
+ return serverlistmgr.favorites
+end
+
+--------------------------------------------------------------------------------
+function serverlistmgr.add_favorite(new_favorite)
+ assert(type(new_favorite.port) == "number")
+
+ -- Whitelist favorite keys
+ new_favorite = {
+ name = new_favorite.name,
+ address = new_favorite.address,
+ port = new_favorite.port,
+ description = new_favorite.description,
+ }
+
+ local favorites = serverlistmgr.get_favorites()
+ delete_favorite(favorites, new_favorite)
+ table.insert(favorites, 1, new_favorite)
+ save_favorites(favorites)
+end
+
+--------------------------------------------------------------------------------
+function serverlistmgr.delete_favorite(del_favorite)
+ local favorites = serverlistmgr.get_favorites()
+ delete_favorite(favorites, del_favorite)
+ save_favorites(favorites)
+end
diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua
index c2b7e503a..a34dd58bb 100644
--- a/builtin/mainmenu/tab_credits.lua
+++ b/builtin/mainmenu/tab_credits.lua
@@ -23,28 +23,37 @@ local core_developers = {
"Nathanaël Courant (Nore/Ekdohibs) <nore@mesecons.net>",
"Loic Blot (nerzhul/nrz) <loic.blot@unix-experience.fr>",
"paramat",
- "Auke Kok (sofar) <sofar@foo-projects.org>",
"Andrew Ward (rubenwardy) <rw@rubenwardy.com>",
"Krock/SmallJoker <mk939@ymail.com>",
"Lars Hofhansl <larsh@apache.org>",
+ "Pierre-Yves Rollo <dev@pyrollo.com>",
+ "v-rob <robinsonvincent89@gmail.com>",
}
+-- For updating active/previous contributors, see the script in ./util/gather_git_credits.py
+
local active_contributors = {
- "Hugues Ross [Formspecs]",
+ "Wuzzy [devtest game, visual corrections]",
+ "Zughy [Visual improvements, various fixes]",
"Maksim (MoNTE48) [Android]",
- "DS [Formspecs]",
- "pyrollo [Formspecs: Hypertext]",
- "v-rob [Formspecs]",
- "Jordach [set_sky]",
- "random-geek [Formspecs]",
- "Wuzzy [Pathfinder, builtin, translations]",
- "ANAND (ClobberXD) [Fixes, per-player FOV]",
- "Warr1024 [Fixes]",
- "Paul Ouellette (pauloue) [Fixes, Script API]",
- "Jean-Patrick G (kilbith) <jeanpatrick.guerrero@gmail.com> [Audiovisuals]",
- "HybridDog [Script API]",
+ "numzero [Graphics and rendering]",
+ "appgurueu [Various internal fixes]",
+ "Desour [Formspec and vector API changes]",
+ "HybridDog [Rendering fixes and documentation]",
+ "Hugues Ross [Graphics-related improvements]",
+ "ANAND (ClobberXD) [Mouse buttons rebinding]",
+ "luk3yx [Fixes]",
+ "hecks [Audiovisuals, Lua API]",
+ "LoneWolfHT [Object crosshair, documentation fixes]",
+ "Lejo [Server-related improvements]",
+ "EvidenceB [Compass HUD element]",
+ "Paul Ouellette (pauloue) [Lua API, documentation]",
+ "TheTermos [Collision detection, physics]",
+ "David CARLIER [Unix & Haiku build fixes]",
"dcbrwn [Object shading]",
- "srifqi [Fixes]",
+ "Elias Fleckenstein [API features/fixes]",
+ "Jean-Patrick Guerrero (kilbith) [model element, visual fixes]",
+ "k.h.lai [Memory leak fixes, documentation]",
}
local previous_core_developers = {
@@ -60,30 +69,23 @@ local previous_core_developers = {
"sapier",
"Zeno",
"ShadowNinja <shadowninja@minetest.net>",
+ "Auke Kok (sofar) <sofar@foo-projects.org>",
}
local previous_contributors = {
"Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net> [Minetest Logo]",
- "Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
"red-001 <red-001@outlook.ie>",
- "numberZero [Audiovisuals: meshgen]",
"Giuseppe Bilotta",
+ "Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
"MirceaKitsune <mirceakitsune@gmail.com>",
"Constantin Wenger (SpeedProg)",
"Ciaran Gultnieks (CiaranG)",
"stujones11 [Android UX improvements]",
- "Jeija <jeija@mesecons.net> [HTTP, particles]",
- "Vincent Glize (Dumbeldor) [Cleanups, CSM APIs]",
- "Ben Deutsch [Rendering, Fixes, SQLite auth]",
- "TeTpaAka [Hand overriding, nametag colors]",
- "Rui [Sound Pitch]",
- "Duane Robertson <duane@duanerobertson.com> [MGValleys]",
- "Raymoo [Tool Capabilities]",
"Rogier <rogier777@gmail.com> [Fixes]",
"Gregory Currie (gregorycu) [optimisation]",
- "TriBlade9 <triblade9@mail.com> [Audiovisuals]",
- "T4im [Profiler]",
- "Jurgen Doser (doserj) <jurgen.doser@gmail.com>",
+ "srifqi [Fixes]",
+ "JacobF",
+ "Jeija <jeija@mesecons.net> [HTTP, particles]",
}
local function buildCreditList(source)
@@ -100,9 +102,10 @@ return {
cbf_formspec = function(tabview, name, tabdata)
local logofile = defaulttexturedir .. "logo.png"
local version = core.get_version()
- return "image[0.5,1;" .. core.formspec_escape(logofile) .. "]" ..
- "label[0.5,2.8;" .. version.project .. " " .. version.string .. "]" ..
- "button[0.5,3;2,2;homepage;minetest.net]" ..
+ local fs = "image[0.75,0.5;2.2,2.2;" .. core.formspec_escape(logofile) .. "]" ..
+ "style[label_button;border=false]" ..
+ "button[0.5,2;2.5,2;label_button;" .. version.project .. " " .. version.string .. "]" ..
+ "button[0.75,2.75;2,2;homepage;minetest.net]" ..
"tablecolumns[color;text]" ..
"tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
"table[3.5,-0.25;8.5,6.05;list_credits;" ..
@@ -115,10 +118,23 @@ return {
"#FFFF00," .. fgettext("Previous Contributors") .. ",," ..
buildCreditList(previous_contributors) .. "," ..
";1]"
+
+ if PLATFORM ~= "Android" then
+ fs = fs .. "tooltip[userdata;" ..
+ fgettext("Opens the directory that contains user-provided worlds, games, mods,\n" ..
+ "and texture packs in a file manager / explorer.") .. "]"
+ fs = fs .. "button[0,4.75;3.5,1;userdata;" .. fgettext("Open User Data Directory") .. "]"
+ end
+
+ return fs
end,
cbf_button_handler = function(this, fields, name, tabdata)
if fields.homepage then
core.open_url("https://www.minetest.net")
end
+
+ if fields.userdata then
+ core.open_dir(core.get_user_path())
+ end
end,
}
diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua
index a21cf12b1..0e06c3bef 100644
--- a/builtin/mainmenu/tab_local.lua
+++ b/builtin/mainmenu/tab_local.lua
@@ -18,6 +18,7 @@
local enable_gamebar = PLATFORM ~= "Android"
local current_game, singleplayer_refresh_gamebar
+
if enable_gamebar then
function current_game()
local last_game_id = core.settings:get("menu_last_game")
@@ -114,45 +115,44 @@ local function get_formspec(tabview, name, tabdata)
)
retval = retval ..
- "button[4,3.95;2.6,1;world_delete;".. fgettext("Delete") .. "]" ..
- "button[6.5,3.95;2.8,1;world_configure;".. fgettext("Configure") .. "]" ..
- "button[9.2,3.95;2.5,1;world_create;".. fgettext("New") .. "]" ..
- "label[4,-0.25;".. fgettext("Select World:") .. "]"..
- "checkbox[0.25,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
+ "button[3.9,3.8;2.8,1;world_delete;".. fgettext("Delete") .. "]" ..
+ "button[6.55,3.8;2.8,1;world_configure;".. fgettext("Select Mods") .. "]" ..
+ "button[9.2,3.8;2.8,1;world_create;".. fgettext("New") .. "]" ..
+ "label[3.9,-0.05;".. fgettext("Select World:") .. "]"..
+ "checkbox[0,-0.20;cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
dump(core.settings:get_bool("creative_mode")) .. "]"..
- "checkbox[0.25,0.7;cb_enable_damage;".. fgettext("Enable Damage") .. ";" ..
+ "checkbox[0,0.25;cb_enable_damage;".. fgettext("Enable Damage") .. ";" ..
dump(core.settings:get_bool("enable_damage")) .. "]"..
- "checkbox[0.25,1.15;cb_server;".. fgettext("Host Server") ..";" ..
+ "checkbox[0,0.7;cb_server;".. fgettext("Host Server") ..";" ..
dump(core.settings:get_bool("enable_server")) .. "]" ..
- "textlist[4,0.25;7.5,3.7;sp_worlds;" ..
+ "textlist[3.9,0.4;7.9,3.45;sp_worlds;" ..
menu_render_worldlist() ..
";" .. index .. "]"
if core.settings:get_bool("enable_server") then
retval = retval ..
- "button[8.5,4.8;3.2,1;play;".. fgettext("Host Game") .. "]" ..
- "checkbox[0.25,1.6;cb_server_announce;" .. fgettext("Announce Server") .. ";" ..
+ "button[7.9,4.75;4.1,1;play;".. fgettext("Host Game") .. "]" ..
+ "checkbox[0,1.15;cb_server_announce;" .. fgettext("Announce Server") .. ";" ..
dump(core.settings:get_bool("server_announce")) .. "]" ..
- "label[0.25,2.2;" .. fgettext("Name/Password") .. "]" ..
- "field[0.55,3.2;3.5,0.5;te_playername;;" ..
+ "field[0.3,2.85;3.8,0.5;te_playername;" .. fgettext("Name") .. ";" ..
core.formspec_escape(core.settings:get("name")) .. "]" ..
- "pwdfield[0.55,4;3.5,0.5;te_passwd;]"
+ "pwdfield[0.3,4.05;3.8,0.5;te_passwd;" .. fgettext("Password") .. "]"
local bind_addr = core.settings:get("bind_address")
if bind_addr ~= nil and bind_addr ~= "" then
retval = retval ..
- "field[0.55,5.2;2.25,0.5;te_serveraddr;" .. fgettext("Bind Address") .. ";" ..
+ "field[0.3,5.25;2.5,0.5;te_serveraddr;" .. fgettext("Bind Address") .. ";" ..
core.formspec_escape(core.settings:get("bind_address")) .. "]" ..
- "field[2.8,5.2;1.25,0.5;te_serverport;" .. fgettext("Port") .. ";" ..
+ "field[2.85,5.25;1.25,0.5;te_serverport;" .. fgettext("Port") .. ";" ..
core.formspec_escape(core.settings:get("port")) .. "]"
else
retval = retval ..
- "field[0.55,5.2;3.5,0.5;te_serverport;" .. fgettext("Server Port") .. ";" ..
+ "field[0.3,5.25;3.8,0.5;te_serverport;" .. fgettext("Server Port") .. ";" ..
core.formspec_escape(core.settings:get("port")) .. "]"
end
else
retval = retval ..
- "button[8.5,4.8;3.2,1;play;".. fgettext("Play Game") .. "]"
+ "button[7.9,4.75;4.1,1;play;" .. fgettext("Play Game") .. "]"
end
return retval
diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua
index 7985fd84a..e6748ed88 100644
--- a/builtin/mainmenu/tab_online.lua
+++ b/builtin/mainmenu/tab_online.lua
@@ -20,11 +20,11 @@ local function get_formspec(tabview, name, tabdata)
-- Update the cached supported proto info,
-- it may have changed after a change by the settings menu.
common_update_cached_supp_proto()
- local fav_selected
+ local selected
if menudata.search_result then
- fav_selected = menudata.search_result[tabdata.fav_selected]
+ selected = menudata.search_result[tabdata.selected]
else
- fav_selected = menudata.favorites[tabdata.fav_selected]
+ selected = serverlistmgr.servers[tabdata.selected]
end
if not tabdata.search_for then
@@ -34,7 +34,8 @@ local function get_formspec(tabview, name, tabdata)
local retval =
-- Search
"field[0.15,0.075;5.91,1;te_search;;" .. core.formspec_escape(tabdata.search_for) .. "]" ..
- "button[5.62,-0.25;1.5,1;btn_mp_search;" .. fgettext("Search") .. "]" ..
+ "image_button[5.63,-.165;.83,.83;" .. core.formspec_escape(defaulttexturedir .. "search.png") .. ";btn_mp_search;]" ..
+ "image_button[6.3,-.165;.83,.83;" .. core.formspec_escape(defaulttexturedir .. "clear.png") .. ";btn_mp_clear;]" ..
"image_button[6.97,-.165;.83,.83;" .. core.formspec_escape(defaulttexturedir .. "refresh.png")
.. ";btn_mp_refresh;]" ..
@@ -57,18 +58,18 @@ local function get_formspec(tabview, name, tabdata)
-- Connect
"button[9.88,4.9;2.3,1;btn_mp_connect;" .. fgettext("Connect") .. "]"
- if tabdata.fav_selected and fav_selected then
+ if tabdata.selected and selected then
if gamedata.fav then
retval = retval .. "button[7.73,4.9;2.3,1;btn_delete_favorite;" ..
fgettext("Del. Favorite") .. "]"
end
- if fav_selected.description then
+ if selected.description then
retval = retval .. "textarea[8.1,2.3;4.23,2.9;;;" ..
core.formspec_escape((gamedata.serverdescription or ""), true) .. "]"
end
end
- --favourites
+ --favorites
retval = retval .. "tablecolumns[" ..
image_column(fgettext("Favorite"), "favorite") .. ";" ..
image_column(fgettext("Ping")) .. ",padding=0.25;" ..
@@ -82,13 +83,12 @@ local function get_formspec(tabview, name, tabdata)
image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" ..
"color,span=1;" ..
"text,padding=1]" ..
- "table[-0.15,0.6;7.75,5.15;favourites;"
+ "table[-0.15,0.6;7.75,5.15;favorites;"
if menudata.search_result then
+ local favs = serverlistmgr.get_favorites()
for i = 1, #menudata.search_result do
- local favs = core.get_favorites("local")
local server = menudata.search_result[i]
-
for fav_id = 1, #favs do
if server.address == favs[fav_id].address and
server.port == favs[fav_id].port then
@@ -102,29 +102,30 @@ local function get_formspec(tabview, name, tabdata)
retval = retval .. render_serverlist_row(server, server.is_favorite)
end
- elseif #menudata.favorites > 0 then
- local favs = core.get_favorites("local")
+ elseif #serverlistmgr.servers > 0 then
+ local favs = serverlistmgr.get_favorites()
if #favs > 0 then
for i = 1, #favs do
- for j = 1, #menudata.favorites do
- if menudata.favorites[j].address == favs[i].address and
- menudata.favorites[j].port == favs[i].port then
- table.insert(menudata.favorites, i, table.remove(menudata.favorites, j))
+ for j = 1, #serverlistmgr.servers do
+ if serverlistmgr.servers[j].address == favs[i].address and
+ serverlistmgr.servers[j].port == favs[i].port then
+ table.insert(serverlistmgr.servers, i, table.remove(serverlistmgr.servers, j))
+ end
end
- end
- if favs[i].address ~= menudata.favorites[i].address then
- table.insert(menudata.favorites, i, favs[i])
+ if favs[i].address ~= serverlistmgr.servers[i].address then
+ table.insert(serverlistmgr.servers, i, favs[i])
end
end
end
- retval = retval .. render_serverlist_row(menudata.favorites[1], (#favs > 0))
- for i = 2, #menudata.favorites do
- retval = retval .. "," .. render_serverlist_row(menudata.favorites[i], (i <= #favs))
+
+ retval = retval .. render_serverlist_row(serverlistmgr.servers[1], (#favs > 0))
+ for i = 2, #serverlistmgr.servers do
+ retval = retval .. "," .. render_serverlist_row(serverlistmgr.servers[i], (i <= #favs))
end
end
- if tabdata.fav_selected then
- retval = retval .. ";" .. tabdata.fav_selected .. "]"
+ if tabdata.selected then
+ retval = retval .. ";" .. tabdata.selected .. "]"
else
retval = retval .. ";0]"
end
@@ -134,21 +135,20 @@ end
--------------------------------------------------------------------------------
local function main_button_handler(tabview, fields, name, tabdata)
- local serverlist = menudata.search_result or menudata.favorites
+ local serverlist = menudata.search_result or serverlistmgr.servers
if fields.te_name then
gamedata.playername = fields.te_name
core.settings:set("name", fields.te_name)
end
- if fields.favourites then
- local event = core.explode_table_event(fields.favourites)
+ if fields.favorites then
+ local event = core.explode_table_event(fields.favorites)
local fav = serverlist[event.row]
if event.type == "DCL" then
if event.row <= #serverlist then
- if menudata.favorites_is_public and
- not is_server_protocol_compat_or_error(
+ if not is_server_protocol_compat_or_error(
fav.proto_min, fav.proto_max) then
return true
end
@@ -177,7 +177,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
if event.type == "CHG" then
if event.row <= #serverlist then
gamedata.fav = false
- local favs = core.get_favorites("local")
+ local favs = serverlistmgr.get_favorites()
local address = fav.address
local port = fav.port
gamedata.serverdescription = fav.description
@@ -193,28 +193,28 @@ local function main_button_handler(tabview, fields, name, tabdata)
core.settings:set("address", address)
core.settings:set("remote_port", port)
end
- tabdata.fav_selected = event.row
+ tabdata.selected = event.row
end
return true
end
end
if fields.key_up or fields.key_down then
- local fav_idx = core.get_table_index("favourites")
+ local fav_idx = core.get_table_index("favorites")
local fav = serverlist[fav_idx]
if fav_idx then
if fields.key_up and fav_idx > 1 then
fav_idx = fav_idx - 1
- elseif fields.key_down and fav_idx < #menudata.favorites then
+ elseif fields.key_down and fav_idx < #serverlistmgr.servers then
fav_idx = fav_idx + 1
end
else
fav_idx = 1
end
- if not menudata.favorites or not fav then
- tabdata.fav_selected = 0
+ if not serverlistmgr.servers or not fav then
+ tabdata.selected = 0
return true
end
@@ -226,29 +226,35 @@ local function main_button_handler(tabview, fields, name, tabdata)
core.settings:set("remote_port", port)
end
- tabdata.fav_selected = fav_idx
+ tabdata.selected = fav_idx
return true
end
if fields.btn_delete_favorite then
- local current_favourite = core.get_table_index("favourites")
- if not current_favourite then return end
+ local current_favorite = core.get_table_index("favorites")
+ if not current_favorite then return end
- core.delete_favorite(current_favourite)
- asyncOnlineFavourites()
- tabdata.fav_selected = nil
+ serverlistmgr.delete_favorite(serverlistmgr.servers[current_favorite])
+ serverlistmgr.sync()
+ tabdata.selected = nil
core.settings:set("address", "")
core.settings:set("remote_port", "30000")
return true
end
+ if fields.btn_mp_clear then
+ tabdata.search_for = ""
+ menudata.search_result = nil
+ return true
+ end
+
if fields.btn_mp_search or fields.key_enter_field == "te_search" then
- tabdata.fav_selected = 1
+ tabdata.selected = 1
local input = fields.te_search:lower()
tabdata.search_for = fields.te_search
- if #menudata.favorites < 2 then
+ if #serverlistmgr.servers < 2 then
return true
end
@@ -268,8 +274,8 @@ local function main_button_handler(tabview, fields, name, tabdata)
-- Search the serverlist
local search_result = {}
- for i = 1, #menudata.favorites do
- local server = menudata.favorites[i]
+ for i = 1, #serverlistmgr.servers do
+ local server = serverlistmgr.servers[i]
local found = 0
for k = 1, #keywords do
local keyword = keywords[k]
@@ -286,7 +292,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
end
end
if found > 0 then
- local points = (#menudata.favorites - i) / 5 + found
+ local points = (#serverlistmgr.servers - i) / 5 + found
server.points = points
table.insert(search_result, server)
end
@@ -305,7 +311,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
end
if fields.btn_mp_refresh then
- asyncOnlineFavourites()
+ serverlistmgr.sync()
return true
end
@@ -314,30 +320,36 @@ local function main_button_handler(tabview, fields, name, tabdata)
gamedata.playername = fields.te_name
gamedata.password = fields.te_pwd
gamedata.address = fields.te_address
- gamedata.port = fields.te_port
+ gamedata.port = tonumber(fields.te_port)
gamedata.selected_world = 0
- local fav_idx = core.get_table_index("favourites")
+ local fav_idx = core.get_table_index("favorites")
local fav = serverlist[fav_idx]
if fav_idx and fav_idx <= #serverlist and
- fav.address == fields.te_address and
- fav.port == fields.te_port then
+ fav.address == gamedata.address and
+ fav.port == gamedata.port then
+
+ serverlistmgr.add_favorite(fav)
gamedata.servername = fav.name
gamedata.serverdescription = fav.description
- if menudata.favorites_is_public and
- not is_server_protocol_compat_or_error(
+ if not is_server_protocol_compat_or_error(
fav.proto_min, fav.proto_max) then
return true
end
else
gamedata.servername = ""
gamedata.serverdescription = ""
+
+ serverlistmgr.add_favorite({
+ address = gamedata.address,
+ port = gamedata.port,
+ })
end
- core.settings:set("address", fields.te_address)
- core.settings:set("remote_port", fields.te_port)
+ core.settings:set("address", gamedata.address)
+ core.settings:set("remote_port", gamedata.port)
core.start()
return true
@@ -347,7 +359,7 @@ end
local function on_change(type, old_tab, new_tab)
if type == "LEAVE" then return end
- asyncOnlineFavourites()
+ serverlistmgr.sync()
end
--------------------------------------------------------------------------------
diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua
index 1e5264904..29744048a 100644
--- a/builtin/mainmenu/tab_settings.lua
+++ b/builtin/mainmenu/tab_settings.lua
@@ -122,56 +122,6 @@ local function antialiasing_fname_to_name(fname)
return 0
end
-local function dlg_confirm_reset_formspec(data)
- return "size[8,3]" ..
- "label[1,1;" .. fgettext("Are you sure to reset your singleplayer world?") .. "]" ..
- "button[1,2;2.6,0.5;dlg_reset_singleplayer_confirm;" .. fgettext("Yes") .. "]" ..
- "button[4,2;2.8,0.5;dlg_reset_singleplayer_cancel;" .. fgettext("No") .. "]"
-end
-
-local function dlg_confirm_reset_btnhandler(this, fields, dialogdata)
-
- if fields["dlg_reset_singleplayer_confirm"] ~= nil then
- local worldlist = core.get_worlds()
- local found_singleplayerworld = false
-
- for i = 1, #worldlist do
- if worldlist[i].name == "singleplayerworld" then
- found_singleplayerworld = true
- gamedata.worldindex = i
- end
- end
-
- if found_singleplayerworld then
- core.delete_world(gamedata.worldindex)
- end
-
- core.create_world("singleplayerworld", 1)
- worldlist = core.get_worlds()
-
- for i = 1, #worldlist do
- if worldlist[i].name == "singleplayerworld" then
- gamedata.worldindex = i
- end
- end
- end
-
- this.parent:show()
- this:hide()
- this:delete()
- return true
-end
-
-local function showconfirm_reset(tabview)
- local new_dlg = dialog_create("reset_spworld",
- dlg_confirm_reset_formspec,
- dlg_confirm_reset_btnhandler,
- nil)
- new_dlg:set_parent(tabview)
- tabview:hide()
- new_dlg:show()
-end
-
local function formspec(tabview, name, tabdata)
local tab_string =
"box[0,0;3.75,4.5;#999999]" ..
@@ -204,30 +154,26 @@ local function formspec(tabview, name, tabdata)
"box[8,0;3.75,4.5;#999999]"
local video_driver = core.settings:get("video_driver")
- local shaders_supported = video_driver == "opengl"
- local shaders_enabled = false
- if shaders_supported then
- shaders_enabled = core.settings:get_bool("enable_shaders")
+ local shaders_enabled = core.settings:get_bool("enable_shaders")
+ if video_driver == "opengl" then
tab_string = tab_string ..
"checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders") .. ";"
.. tostring(shaders_enabled) .. "]"
+ elseif video_driver == "ogles2" then
+ tab_string = tab_string ..
+ "checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders (experimental)") .. ";"
+ .. tostring(shaders_enabled) .. "]"
else
core.settings:set_bool("enable_shaders", false)
+ shaders_enabled = false
tab_string = tab_string ..
"label[8.38,0.2;" .. core.colorize("#888888",
fgettext("Shaders (unavailable)")) .. "]"
end
- if core.settings:get("main_menu_style") == "simple" then
- -- 'Reset singleplayer world' only functions with simple menu
- tab_string = tab_string ..
- "button[8,4.75;3.95,1;btn_reset_singleplayer;"
- .. fgettext("Reset singleplayer world") .. "]"
- else
- tab_string = tab_string ..
- "button[8,4.75;3.95,1;btn_change_keys;"
- .. fgettext("Change Keys") .. "]"
- end
+ tab_string = tab_string ..
+ "button[8,4.75;3.95,1;btn_change_keys;"
+ .. fgettext("Change Keys") .. "]"
tab_string = tab_string ..
"button[0,4.75;3.95,1;btn_advanced_settings;"
@@ -244,35 +190,23 @@ local function formspec(tabview, name, tabdata)
if shaders_enabled then
tab_string = tab_string ..
- "checkbox[8.25,0.5;cb_bumpmapping;" .. fgettext("Bump Mapping") .. ";"
- .. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" ..
- "checkbox[8.25,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
+ "checkbox[8.25,0.5;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
.. dump(core.settings:get_bool("tone_mapping")) .. "]" ..
- "checkbox[8.25,1.5;cb_generate_normalmaps;" .. fgettext("Generate Normal Maps") .. ";"
- .. dump(core.settings:get_bool("generate_normalmaps")) .. "]" ..
- "checkbox[8.25,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";"
- .. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" ..
- "checkbox[8.25,2.5;cb_waving_water;" .. fgettext("Waving Liquids") .. ";"
+ "checkbox[8.25,1;cb_waving_water;" .. fgettext("Waving Liquids") .. ";"
.. dump(core.settings:get_bool("enable_waving_water")) .. "]" ..
- "checkbox[8.25,3;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
+ "checkbox[8.25,1.5;cb_waving_leaves;" .. fgettext("Waving Leaves") .. ";"
.. dump(core.settings:get_bool("enable_waving_leaves")) .. "]" ..
- "checkbox[8.25,3.5;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
+ "checkbox[8.25,2;cb_waving_plants;" .. fgettext("Waving Plants") .. ";"
.. dump(core.settings:get_bool("enable_waving_plants")) .. "]"
else
tab_string = tab_string ..
"label[8.38,0.7;" .. core.colorize("#888888",
- fgettext("Bump Mapping")) .. "]" ..
- "label[8.38,1.2;" .. core.colorize("#888888",
fgettext("Tone Mapping")) .. "]" ..
- "label[8.38,1.7;" .. core.colorize("#888888",
- fgettext("Generate Normal Maps")) .. "]" ..
- "label[8.38,2.2;" .. core.colorize("#888888",
- fgettext("Parallax Occlusion")) .. "]" ..
- "label[8.38,2.7;" .. core.colorize("#888888",
+ "label[8.38,1.2;" .. core.colorize("#888888",
fgettext("Waving Liquids")) .. "]" ..
- "label[8.38,3.2;" .. core.colorize("#888888",
+ "label[8.38,1.7;" .. core.colorize("#888888",
fgettext("Waving Leaves")) .. "]" ..
- "label[8.38,3.7;" .. core.colorize("#888888",
+ "label[8.38,2.2;" .. core.colorize("#888888",
fgettext("Waving Plants")) .. "]"
end
@@ -324,22 +258,10 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
end
return true
end
- if fields["cb_bumpmapping"] then
- core.settings:set("enable_bumpmapping", fields["cb_bumpmapping"])
- return true
- end
if fields["cb_tonemapping"] then
core.settings:set("tone_mapping", fields["cb_tonemapping"])
return true
end
- if fields["cb_generate_normalmaps"] then
- core.settings:set("generate_normalmaps", fields["cb_generate_normalmaps"])
- return true
- end
- if fields["cb_parallax"] then
- core.settings:set("enable_parallax_occlusion", fields["cb_parallax"])
- return true
- end
if fields["cb_waving_water"] then
core.settings:set("enable_waving_water", fields["cb_waving_water"])
return true
@@ -359,10 +281,6 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
core.settings:set("touchtarget", fields["cb_touchscreen_target"])
return true
end
- if fields["btn_reset_singleplayer"] then
- showconfirm_reset(this)
- return true
- end
--Note dropdowns have to be handled LAST!
local ddhandled = false
diff --git a/builtin/mainmenu/tab_simple_main.lua b/builtin/mainmenu/tab_simple_main.lua
deleted file mode 100644
index 7ec95158a..000000000
--- a/builtin/mainmenu/tab_simple_main.lua
+++ /dev/null
@@ -1,220 +0,0 @@
---Minetest
---Copyright (C) 2013 sapier
---
---This program is free software; you can redistribute it and/or modify
---it under the terms of the GNU Lesser General Public License as published by
---the Free Software Foundation; either version 2.1 of the License, or
---(at your option) any later version.
---
---This program is distributed in the hope that it will be useful,
---but WITHOUT ANY WARRANTY; without even the implied warranty of
---MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
---GNU Lesser General Public License for more details.
---
---You should have received a copy of the GNU Lesser General Public License along
---with this program; if not, write to the Free Software Foundation, Inc.,
---51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
---------------------------------------------------------------------------------
-local function get_formspec(tabview, name, tabdata)
- -- Update the cached supported proto info,
- -- it may have changed after a change by the settings menu.
- common_update_cached_supp_proto()
- local fav_selected = menudata.favorites[tabdata.fav_selected]
-
- local retval =
- "label[9.5,0;".. fgettext("Name / Password") .. "]" ..
- "field[0.25,3.35;5.5,0.5;te_address;;" ..
- core.formspec_escape(core.settings:get("address")) .."]" ..
- "field[5.75,3.35;2.25,0.5;te_port;;" ..
- core.formspec_escape(core.settings:get("remote_port")) .."]" ..
- "button[10,2.6;2,1.5;btn_mp_connect;".. fgettext("Connect") .. "]" ..
- "field[9.8,1;2.6,0.5;te_name;;" ..
- core.formspec_escape(core.settings:get("name")) .."]" ..
- "pwdfield[9.8,2;2.6,0.5;te_pwd;]"
-
-
- if tabdata.fav_selected and fav_selected then
- if gamedata.fav then
- retval = retval .. "button[7.7,2.6;2.3,1.5;btn_delete_favorite;" ..
- fgettext("Del. Favorite") .. "]"
- end
- end
-
- retval = retval .. "tablecolumns[" ..
- image_column(fgettext("Favorite"), "favorite") .. ";" ..
- image_column(fgettext("Ping"), "") .. ",padding=0.25;" ..
- "color,span=3;" ..
- "text,align=right;" .. -- clients
- "text,align=center,padding=0.25;" .. -- "/"
- "text,align=right,padding=0.25;" .. -- clients_max
- image_column(fgettext("Creative mode"), "creative") .. ",padding=1;" ..
- image_column(fgettext("Damage enabled"), "damage") .. ",padding=0.25;" ..
- image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" ..
- "color,span=1;" ..
- "text,padding=1]" .. -- name
- "table[-0.05,0;9.2,2.75;favourites;"
-
- if #menudata.favorites > 0 then
- local favs = core.get_favorites("local")
- if #favs > 0 then
- for i = 1, #favs do
- for j = 1, #menudata.favorites do
- if menudata.favorites[j].address == favs[i].address and
- menudata.favorites[j].port == favs[i].port then
- table.insert(menudata.favorites, i,
- table.remove(menudata.favorites, j))
- end
- end
- if favs[i].address ~= menudata.favorites[i].address then
- table.insert(menudata.favorites, i, favs[i])
- end
- end
- end
- retval = retval .. render_serverlist_row(menudata.favorites[1], (#favs > 0))
- for i = 2, #menudata.favorites do
- retval = retval .. "," .. render_serverlist_row(menudata.favorites[i], (i <= #favs))
- end
- end
-
- if tabdata.fav_selected then
- retval = retval .. ";" .. tabdata.fav_selected .. "]"
- else
- retval = retval .. ";0]"
- end
-
- -- separator
- retval = retval .. "box[-0.28,3.75;12.4,0.1;#FFFFFF]"
-
- -- checkboxes
- retval = retval ..
- "checkbox[8.0,3.9;cb_creative;".. fgettext("Creative Mode") .. ";" ..
- dump(core.settings:get_bool("creative_mode")) .. "]"..
- "checkbox[8.0,4.4;cb_damage;".. fgettext("Enable Damage") .. ";" ..
- dump(core.settings:get_bool("enable_damage")) .. "]"
- -- buttons
- retval = retval ..
- "button[0,3.7;8,1.5;btn_start_singleplayer;" .. fgettext("Start Singleplayer") .. "]" ..
- "button[0,4.5;8,1.5;btn_config_sp_world;" .. fgettext("Config mods") .. "]"
-
- return retval
-end
-
---------------------------------------------------------------------------------
-local function main_button_handler(tabview, fields, name, tabdata)
- if fields.btn_start_singleplayer then
- gamedata.selected_world = gamedata.worldindex
- gamedata.singleplayer = true
- core.start()
- return true
- end
-
- if fields.favourites then
- local event = core.explode_table_event(fields.favourites)
- if event.type == "CHG" then
- if event.row <= #menudata.favorites then
- gamedata.fav = false
- local favs = core.get_favorites("local")
- local fav = menudata.favorites[event.row]
- local address = fav.address
- local port = fav.port
- gamedata.serverdescription = fav.description
-
- for i = 1, #favs do
- if fav.address == favs[i].address and
- fav.port == favs[i].port then
- gamedata.fav = true
- end
- end
-
- if address and port then
- core.settings:set("address", address)
- core.settings:set("remote_port", port)
- end
- tabdata.fav_selected = event.row
- end
- return true
- end
- end
-
- if fields.btn_delete_favorite then
- local current_favourite = core.get_table_index("favourites")
- if not current_favourite then return end
-
- core.delete_favorite(current_favourite)
- asyncOnlineFavourites()
- tabdata.fav_selected = nil
-
- core.settings:set("address", "")
- core.settings:set("remote_port", "30000")
- return true
- end
-
- if fields.cb_creative then
- core.settings:set("creative_mode", fields.cb_creative)
- return true
- end
-
- if fields.cb_damage then
- core.settings:set("enable_damage", fields.cb_damage)
- return true
- end
-
- if fields.btn_mp_connect or fields.key_enter then
- gamedata.playername = fields.te_name
- gamedata.password = fields.te_pwd
- gamedata.address = fields.te_address
- gamedata.port = fields.te_port
- local fav_idx = core.get_textlist_index("favourites")
-
- if fav_idx and fav_idx <= #menudata.favorites and
- menudata.favorites[fav_idx].address == fields.te_address and
- menudata.favorites[fav_idx].port == fields.te_port then
- local fav = menudata.favorites[fav_idx]
- gamedata.servername = fav.name
- gamedata.serverdescription = fav.description
-
- if menudata.favorites_is_public and
- not is_server_protocol_compat_or_error(
- fav.proto_min, fav.proto_max) then
- return true
- end
- else
- gamedata.servername = ""
- gamedata.serverdescription = ""
- end
-
- gamedata.selected_world = 0
-
- core.settings:set("address", fields.te_address)
- core.settings:set("remote_port", fields.te_port)
-
- core.start()
- return true
- end
-
- if fields.btn_config_sp_world then
- local configdialog = create_configure_world_dlg(1)
- if configdialog then
- configdialog:set_parent(tabview)
- tabview:hide()
- configdialog:show()
- end
- return true
- end
-end
-
---------------------------------------------------------------------------------
-local function on_activate(type,old_tab,new_tab)
- if type == "LEAVE" then return end
- asyncOnlineFavourites()
-end
-
---------------------------------------------------------------------------------
-return {
- name = "main",
- caption = fgettext("Main"),
- cbf_formspec = get_formspec,
- cbf_button_handler = main_button_handler,
- on_change = on_activate
-}
diff --git a/builtin/mainmenu/tests/favorites_wellformed.txt b/builtin/mainmenu/tests/favorites_wellformed.txt
new file mode 100644
index 000000000..8b87b4398
--- /dev/null
+++ b/builtin/mainmenu/tests/favorites_wellformed.txt
@@ -0,0 +1,29 @@
+[server]
+
+127.0.0.1
+30000
+
+
+[server]
+
+localhost
+30000
+
+
+[server]
+
+vps.rubenwardy.com
+30001
+
+
+[server]
+
+gundul.ddnss.de
+39155
+
+
+[server]
+VanessaE's Dreambuilder creative Server
+daconcepts.com
+30000
+VanessaE's Dreambuilder creative-mode server. Lots of mods, whitelisted buckets.
diff --git a/builtin/mainmenu/tests/serverlistmgr_spec.lua b/builtin/mainmenu/tests/serverlistmgr_spec.lua
new file mode 100644
index 000000000..148e9b794
--- /dev/null
+++ b/builtin/mainmenu/tests/serverlistmgr_spec.lua
@@ -0,0 +1,36 @@
+_G.core = {}
+_G.unpack = table.unpack
+_G.serverlistmgr = {}
+
+dofile("builtin/common/misc_helpers.lua")
+dofile("builtin/mainmenu/serverlistmgr.lua")
+
+local base = "builtin/mainmenu/tests/"
+
+describe("legacy favorites", function()
+ it("loads well-formed correctly", function()
+ local favs = serverlistmgr.read_legacy_favorites(base .. "favorites_wellformed.txt")
+
+ local expected = {
+ {
+ address = "127.0.0.1",
+ port = 30000,
+ },
+
+ { address = "localhost", port = 30000 },
+
+ { address = "vps.rubenwardy.com", port = 30001 },
+
+ { address = "gundul.ddnss.de", port = 39155 },
+
+ {
+ address = "daconcepts.com",
+ port = 30000,
+ name = "VanessaE's Dreambuilder creative Server",
+ description = "VanessaE's Dreambuilder creative-mode server. Lots of mods, whitelisted buckets."
+ },
+ }
+
+ assert.same(expected, favs)
+ end)
+end)
diff --git a/builtin/profiler/instrumentation.lua b/builtin/profiler/instrumentation.lua
index 237f048fb..6b951a2c2 100644
--- a/builtin/profiler/instrumentation.lua
+++ b/builtin/profiler/instrumentation.lua
@@ -160,6 +160,7 @@ local function init()
-- Simple iteration would ignore lookup via __index.
local entity_instrumentation = {
"on_activate",
+ "on_deactivate",
"on_step",
"on_punch",
"on_rightclick",
diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt
index c787aea2c..f800f71ab 100644
--- a/builtin/settingtypes.txt
+++ b/builtin/settingtypes.txt
@@ -110,9 +110,9 @@ doubletap_jump (Double tap jump for fly) bool false
# enabled.
always_fly_fast (Always fly and fast) bool true
-# The time in seconds it takes between repeated right clicks when holding the right
-# mouse button.
-repeat_rightclick_time (Rightclick repetition interval) float 0.25 0.001
+# The time in seconds it takes between repeated node placements when holding
+# the place button.
+repeat_place_time (Place repetition interval) float 0.25 0.001
# Automatically jump up single-node obstacles.
autojump (Automatic jumping) bool false
@@ -152,6 +152,9 @@ joystick_type (Joystick type) enum auto auto,generic,xbox
# when holding down a joystick button combination.
repeat_joystick_button_time (Joystick button repetition interval) float 0.17 0.001
+# The deadzone of the joystick
+joystick_deadzone (Joystick deadzone) int 2048
+
# The sensitivity of the joystick axes for moving the
# ingame view frustum around.
joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170
@@ -182,6 +185,14 @@ keymap_jump (Jump key) key KEY_SPACE
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_sneak (Sneak key) key KEY_LSHIFT
+# Key for digging.
+# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
+keymap_dig (Dig key) key KEY_LBUTTON
+
+# Key for placing.
+# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
+keymap_place (Place key) key KEY_RBUTTON
+
# Key for opening the inventory.
# See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3
keymap_inventory (Inventory key) key KEY_KEY_I
@@ -440,6 +451,10 @@ keymap_decrease_viewing_range_min (View range decrease key) key -
[**Basic]
+# Whether nametag backgrounds should be shown by default.
+# Mods may still set a background.
+show_nametag_backgrounds (Show nametag backgrounds by default) bool true
+
# Enable vertex buffer objects.
# This should greatly improve graphics performance.
enable_vbo (VBO) bool true
@@ -505,8 +520,13 @@ texture_clean_transparent (Clean transparent textures) bool false
# texture autoscaling.
texture_min_size (Minimum texture size) int 64
-# Experimental option, might cause visible spaces between blocks
-# when set to higher number than 0.
+# Use multi-sample antialiasing (MSAA) to smooth out block edges.
+# This algorithm smooths out the 3D viewport while keeping the image sharp,
+# but it doesn't affect the insides of textures
+# (which is especially noticeable with transparent textures).
+# Visible spaces appear between nodes when shaders are disabled.
+# If set to 0, MSAA is disabled.
+# A restart is required after changing this option.
fsaa (FSAA) enum 0 0,1,2,4,8,16
# Undersampling is similar to using a lower screen resolution, but it applies
@@ -533,43 +553,6 @@ shader_path (Shader path) path
# enhanced, highlights and shadows are gradually compressed.
tone_mapping (Filmic tone mapping) bool false
-[***Bumpmapping]
-
-# Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack
-# or need to be auto-generated.
-# Requires shaders to be enabled.
-enable_bumpmapping (Bumpmapping) bool false
-
-# Enables on the fly normalmap generation (Emboss effect).
-# Requires bumpmapping to be enabled.
-generate_normalmaps (Generate normalmaps) bool false
-
-# Strength of generated normalmaps.
-normalmaps_strength (Normalmaps strength) float 0.6
-
-# Defines sampling step of texture.
-# A higher value results in smoother normal maps.
-normalmaps_smooth (Normalmaps sampling) int 0 0 2
-
-[***Parallax Occlusion]
-
-# Enables parallax occlusion mapping.
-# Requires shaders to be enabled.
-enable_parallax_occlusion (Parallax occlusion) bool false
-
-# 0 = parallax occlusion with slope information (faster).
-# 1 = relief mapping (slower, more accurate).
-parallax_occlusion_mode (Parallax occlusion mode) int 1 0 1
-
-# Number of parallax occlusion iterations.
-parallax_occlusion_iterations (Parallax occlusion iterations) int 4
-
-# Overall scale of parallax occlusion effect.
-parallax_occlusion_scale (Parallax occlusion scale) float 0.08
-
-# Overall bias of parallax occlusion effect, usually scale/2.
-parallax_occlusion_bias (Parallax occlusion bias) float 0.04
-
[***Waving Nodes]
# Set to true to enable waving liquids (like water).
@@ -610,15 +593,15 @@ arm_inertia (Arm inertia) bool true
# to not waste CPU power for no benefit.
fps_max (Maximum FPS) int 60 1
-# Maximum FPS when game is paused.
-pause_fps_max (FPS in pause menu) int 20 1
+# Maximum FPS when the window is not focused, or when the game is paused.
+fps_max_unfocused (FPS when unfocused or paused) int 20 1
# Open the pause menu when the window's focus is lost. Does not pause if a formspec is
# open.
pause_on_lost_focus (Pause on lost window focus) bool false
# View distance in nodes.
-viewing_range (Viewing range) int 100 20 4000
+viewing_range (Viewing range) int 190 20 4000
# Camera 'near clipping plane' distance in nodes, between 0 and 0.25
# Only works on GLES platforms. Most users will not need to change this.
@@ -682,8 +665,8 @@ texture_path (Texture path) path
# The rendering back-end for Irrlicht.
# A restart is required after changing this.
# Note: On Android, stick with OGLES1 if unsure! App may fail to start otherwise.
-# On other platforms, OpenGL is recommended, and it’s the only driver with
-# shader support currently.
+# On other platforms, OpenGL is recommended.
+# Shaders are supported by OpenGL (desktop only) and OGLES2 (experimental)
video_driver (Video driver) enum opengl null,software,burningsvideo,direct3d8,direct3d9,opengl,ogles1,ogles2
# Radius of cloud area stated in number of 64 node cloud squares.
@@ -741,9 +724,11 @@ selectionbox_color (Selection box color) string (0,0,0)
selectionbox_width (Selection box width) int 2 1 5
# Crosshair color (R,G,B).
+# Also controls the object crosshair color
crosshair_color (Crosshair color) string (255,255,255)
# Crosshair alpha (opaqueness, between 0 and 255).
+# Also controls the object crosshair color
crosshair_alpha (Crosshair alpha) int 255 0 255
# Maximum number of recent chat messages to show
@@ -817,7 +802,8 @@ world_aligned_mode (World-aligned textures mode) enum enable disable,enable,forc
autoscale_mode (Autoscaling mode) enum disable disable,enable,force
# Show entity selection boxes
-show_entity_selectionbox (Show entity selection boxes) bool true
+# A restart is required after changing this.
+show_entity_selectionbox (Show entity selection boxes) bool false
[*Menus]
@@ -982,7 +968,7 @@ serverlist_url (Serverlist URL) string servers.minetest.net
# File in client/serverlist/ that contains your favorite servers displayed in the
# Multiplayer Tab.
-serverlist_file (Serverlist file) string favoriteservers.txt
+serverlist_file (Serverlist file) string favoriteservers.json
# Maximum size of the out chat queue.
# 0 to disable queueing and -1 to make the queue size unlimited.
@@ -999,7 +985,7 @@ client_unload_unused_data_timeout (Mapblock unload timeout) int 600
# Maximum number of mapblocks for client to be kept in memory.
# Set to -1 for unlimited amount.
-client_mapblock_limit (Mapblock limit) int 5000
+client_mapblock_limit (Mapblock limit) int 7500
# Whether to show the client debug info (has the same effect as hitting F5).
show_debug (Show debug info) bool false
@@ -1069,6 +1055,13 @@ full_block_send_enable_min_time_from_building (Delay in sending blocks after bui
# client number.
max_packets_per_iteration (Max. packets per iteration) int 1024
+# ZLib compression level to use when sending mapblocks to the client.
+# -1 - Zlib's default compression level
+# 0 - no compresson, fastest
+# 9 - best compression, slowest
+# (levels 1-3 use Zlib's "fast" method, 4-9 use the normal method)
+map_compression_level_net (Map Compression Level for Network Transfer) int -1 -1 9
+
[*Game]
# Default game when creating a new world.
@@ -1096,7 +1089,7 @@ default_stack_max (Default stack size) int 99
# Enable players getting damage and dying.
enable_damage (Damage) bool false
-# Enable creative mode for new created maps.
+# Enable creative mode for all players
creative_mode (Creative) bool false
# A chosen map seed for a new map, leave empty for random.
@@ -1158,17 +1151,17 @@ ask_reconnect_on_crash (Ask to reconnect after crash) bool false
# Setting this larger than active_block_range will also cause the server
# to maintain active objects up to this distance in the direction the
# player is looking. (This can avoid mobs suddenly disappearing from view)
-active_object_send_range_blocks (Active object send range) int 4
+active_object_send_range_blocks (Active object send range) int 8
# The radius of the volume of blocks around every player that is subject to the
# active block stuff, stated in mapblocks (16 nodes).
# In active blocks objects are loaded and ABMs run.
# This is also the minimum range in which active objects (mobs) are maintained.
# This should be configured together with active_object_send_range_blocks.
-active_block_range (Active block range) int 3
+active_block_range (Active block range) int 4
# From how far blocks are sent to clients, stated in mapblocks (16 nodes).
-max_block_send_distance (Max block send distance) int 10
+max_block_send_distance (Max block send distance) int 12
# Maximum number of forceloaded mapblocks.
max_forceloaded_blocks (Maximum forceloaded blocks) int 16
@@ -1241,10 +1234,10 @@ movement_gravity (Gravity) float 9.81
[**Advanced]
# Handling for deprecated Lua API calls:
-# - legacy: (try to) mimic old behaviour (default for release).
-# - log: mimic and log backtrace of deprecated call (default for debug).
+# - none: Do not log deprecated calls
+# - log: mimic and log backtrace of deprecated call (default).
# - error: abort on usage of deprecated call (suggested for mod developers).
-deprecated_lua_api_handling (Deprecated Lua API handling) enum legacy legacy,log,error
+deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,error
# Number of extra blocks that can be loaded by /clearobjects at once.
# This is a trade-off between sqlite transaction overhead and
@@ -1261,6 +1254,13 @@ max_objects_per_block (Maximum objects per block) int 64
# See https://www.sqlite.org/pragma.html#pragma_synchronous
sqlite_synchronous (Synchronous SQLite) enum 2 0,1,2
+# ZLib compression level to use when saving mapblocks to disk.
+# -1 - Zlib's default compression level
+# 0 - no compresson, fastest
+# 9 - best compression, slowest
+# (levels 1-3 use Zlib's "fast" method, 4-9 use the normal method)
+map_compression_level_disk (Map Compression Level for Disk Storage) int 3 -1 9
+
# Length of a server tick and the interval at which objects are generally updated over
# network.
dedicated_server_step (Dedicated server step) float 0.09
@@ -1271,6 +1271,10 @@ active_block_mgmt_interval (Active block management interval) float 2.0
# Length of time between Active Block Modifier (ABM) execution cycles
abm_interval (ABM interval) float 1.0
+# The time budget allowed for ABMs to execute on each step
+# (as a fraction of the ABM Interval)
+abm_time_budget (ABM time budget) float 0.2 0.1 0.9
+
# Length of time between NodeTimer execution cycles
nodetimer_interval (NodeTimer interval) float 0.2
@@ -1431,12 +1435,6 @@ curl_file_download_timeout (cURL file download timeout) int 300000
# Makes DirectX work with LuaJIT. Disable if it causes troubles.
high_precision_fpu (High-precision FPU) bool true
-# Changes the main menu UI:
-# - Full: Multiple singleplayer worlds, game choice, texture pack chooser, etc.
-# - Simple: One singleplayer world, no game or texture pack choosers. May be
-# necessary for smaller screens.
-main_menu_style (Main menu style) enum full full,simple
-
# Replaces the default main menu with a custom one.
main_menu_script (Main menu script) string
@@ -1456,7 +1454,7 @@ mg_name (Mapgen name) enum v7 v7,valleys,carpathian,v5,flat,fractal,singlenode,v
water_level (Water level) int 1
# From how far blocks are generated for clients, stated in mapblocks (16 nodes).
-max_block_generate_distance (Max block generate distance) int 8
+max_block_generate_distance (Max block generate distance) int 10
# Limit of map generation, in nodes, in all 6 directions from (0, 0, 0).
# Only mapchunks completely within the mapgen limit are generated.
@@ -1466,7 +1464,7 @@ mapgen_limit (Map generation limit) int 31000 0 31000
# Global map generation attributes.
# In Mapgen v6 the 'decorations' flag controls all decorations except trees
# and junglegrass, in all other mapgens this flag controls all decorations.
-mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes caves,dungeons,light,decorations,biomes,nocaves,nodungeons,nolight,nodecorations,nobiomes
+mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes,ores caves,dungeons,light,decorations,biomes,ores,nocaves,nodungeons,nolight,nodecorations,nobiomes,noores
[*Biome API temperature and humidity noise parameters]
@@ -1857,7 +1855,7 @@ mgcarpathian_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 50
# Map generation attributes specific to Mapgen Flat.
# Occasional lakes and hills can be added to the flat world.
-mgflat_spflags (Mapgen Flat specific flags) flags nolakes,nohills lakes,hills,nolakes,nohills
+mgflat_spflags (Mapgen Flat specific flags) flags nolakes,nohills,nocaverns lakes,hills,caverns,nolakes,nohills,nocaverns
# Y of flat ground.
mgflat_ground_level (Ground level) int 8
@@ -1901,6 +1899,15 @@ mgflat_hill_threshold (Hill threshold) float 0.45
# Controls steepness/height of hills.
mgflat_hill_steepness (Hill steepness) float 64.0
+# Y-level of cavern upper limit.
+mgflat_cavern_limit (Cavern limit) int -256
+
+# Y-distance over which caverns expand to full size.
+mgflat_cavern_taper (Cavern taper) int 256
+
+# Defines full size of caverns, smaller values create larger caverns.
+mgflat_cavern_threshold (Cavern threshold) float 0.7
+
# Lower Y limit of dungeons.
mgflat_dungeon_ymin (Dungeon minimum Y) int -31000
@@ -1921,6 +1928,9 @@ mgflat_np_cave1 (Cave1 noise) noise_params_3d 0, 12, (61, 61, 61), 52534, 3, 0.5
# Second of two 3D noises that together define tunnels.
mgflat_np_cave2 (Cave2 noise) noise_params_3d 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0
+# 3D noise defining giant caverns.
+mgflat_np_cavern (Cavern noise) noise_params_3d 0, 1, (384, 128, 384), 723, 5, 0.63, 2.0
+
# 3D noise that determines number of dungeons per mapchunk.
mgflat_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), 0, 2, 0.8, 2.0
@@ -2163,15 +2173,15 @@ chunksize (Chunk size) int 5
enable_mapgen_debug_info (Mapgen debug) bool false
# Maximum number of blocks that can be queued for loading.
-emergequeue_limit_total (Absolute limit of queued blocks to emerge) int 512
+emergequeue_limit_total (Absolute limit of queued blocks to emerge) int 1024
# Maximum number of blocks to be queued that are to be loaded from file.
# This limit is enforced per player.
-emergequeue_limit_diskonly (Per-player limit of queued blocks load from disk) int 64
+emergequeue_limit_diskonly (Per-player limit of queued blocks load from disk) int 128
# Maximum number of blocks to be queued that are to be generated.
# This limit is enforced per player.
-emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 64
+emergequeue_limit_generate (Per-player limit of queued blocks to generate) int 128
# Number of emerge threads to use.
# Value 0:
@@ -2197,3 +2207,7 @@ contentdb_url (ContentDB URL) string https://content.minetest.net
# These flags are independent from Minetest versions,
# so see a full list at https://content.minetest.net/help/content_flags/
contentdb_flag_blacklist (ContentDB Flag Blacklist) string nonfree, desktop_default
+
+# Maximum number of concurrent downloads. Downloads exceeding this limit will be queued.
+# This should be lower than curl_parallel_limit.
+contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3
diff --git a/client/shaders/3d_interlaced_merge/opengl_fragment.glsl b/client/shaders/3d_interlaced_merge/opengl_fragment.glsl
index 25945ad7f..7cba61b39 100644
--- a/client/shaders/3d_interlaced_merge/opengl_fragment.glsl
+++ b/client/shaders/3d_interlaced_merge/opengl_fragment.glsl
@@ -6,9 +6,11 @@ uniform sampler2D textureFlags;
#define rightImage normalTexture
#define maskImage textureFlags
+varying mediump vec2 varTexCoord;
+
void main(void)
{
- vec2 uv = gl_TexCoord[0].st;
+ vec2 uv = varTexCoord.st;
vec4 left = texture2D(leftImage, uv).rgba;
vec4 right = texture2D(rightImage, uv).rgba;
vec4 mask = texture2D(maskImage, uv).rgba;
diff --git a/client/shaders/3d_interlaced_merge/opengl_vertex.glsl b/client/shaders/3d_interlaced_merge/opengl_vertex.glsl
index 4e0b2b125..860049481 100644
--- a/client/shaders/3d_interlaced_merge/opengl_vertex.glsl
+++ b/client/shaders/3d_interlaced_merge/opengl_vertex.glsl
@@ -1,6 +1,7 @@
+varying mediump vec2 varTexCoord;
+
void main(void)
{
- gl_TexCoord[0] = gl_MultiTexCoord0;
- gl_Position = gl_Vertex;
- gl_FrontColor = gl_BackColor = gl_Color;
+ varTexCoord = inTexCoord0;
+ gl_Position = inVertexPosition;
}
diff --git a/client/shaders/default_shader/opengl_fragment.glsl b/client/shaders/default_shader/opengl_fragment.glsl
index 925ab6e1d..5018ac6ea 100644
--- a/client/shaders/default_shader/opengl_fragment.glsl
+++ b/client/shaders/default_shader/opengl_fragment.glsl
@@ -1,4 +1,6 @@
+varying lowp vec4 varColor;
+
void main(void)
{
- gl_FragColor = gl_Color;
+ gl_FragColor = varColor;
}
diff --git a/client/shaders/default_shader/opengl_vertex.glsl b/client/shaders/default_shader/opengl_vertex.glsl
index d0b16c8b0..d95a3c2d3 100644
--- a/client/shaders/default_shader/opengl_vertex.glsl
+++ b/client/shaders/default_shader/opengl_vertex.glsl
@@ -1,9 +1,7 @@
-uniform mat4 mWorldViewProj;
+varying lowp vec4 varColor;
void main(void)
{
- gl_TexCoord[0] = gl_MultiTexCoord0;
- gl_Position = mWorldViewProj * gl_Vertex;
-
- gl_FrontColor = gl_BackColor = gl_Color;
+ gl_Position = mWorldViewProj * inVertexPosition;
+ varColor = inVertexColor;
}
diff --git a/client/shaders/minimap_shader/opengl_fragment.glsl b/client/shaders/minimap_shader/opengl_fragment.glsl
index fa4f9cb1a..cef359e8a 100644
--- a/client/shaders/minimap_shader/opengl_fragment.glsl
+++ b/client/shaders/minimap_shader/opengl_fragment.glsl
@@ -2,9 +2,12 @@ uniform sampler2D baseTexture;
uniform sampler2D normalTexture;
uniform vec3 yawVec;
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
+
void main (void)
{
- vec2 uv = gl_TexCoord[0].st;
+ vec2 uv = varTexCoord.st;
//texture sampling rate
const float step = 1.0 / 256.0;
@@ -27,6 +30,6 @@ void main (void)
vec3 color = (1.1 * diffuse + 0.05 * height + 0.5 * specular) * base.rgb;
vec4 col = vec4(color.rgb, base.a);
- col *= gl_Color;
+ col *= varColor;
gl_FragColor = vec4(col.rgb, base.a);
}
diff --git a/client/shaders/minimap_shader/opengl_vertex.glsl b/client/shaders/minimap_shader/opengl_vertex.glsl
index 88f9356d5..1a9491805 100644
--- a/client/shaders/minimap_shader/opengl_vertex.glsl
+++ b/client/shaders/minimap_shader/opengl_vertex.glsl
@@ -1,9 +1,11 @@
-uniform mat4 mWorldViewProj;
uniform mat4 mWorld;
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
+
void main(void)
{
- gl_TexCoord[0] = gl_MultiTexCoord0;
- gl_Position = mWorldViewProj * gl_Vertex;
- gl_FrontColor = gl_BackColor = gl_Color;
+ varTexCoord = inTexCoord0.st;
+ gl_Position = mWorldViewProj * inVertexPosition;
+ varColor = inVertexColor;
}
diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl
index 19e6c2d86..b58095063 100644
--- a/client/shaders/nodes_shader/opengl_fragment.glsl
+++ b/client/shaders/nodes_shader/opengl_fragment.glsl
@@ -1,6 +1,4 @@
uniform sampler2D baseTexture;
-uniform sampler2D normalTexture;
-uniform sampler2D textureFlags;
uniform vec4 skyBgColor;
uniform float fogDistance;
@@ -17,21 +15,18 @@ varying vec3 vPosition;
// cameraOffset + worldPosition (for large coordinates the limits of float
// precision must be considered).
varying vec3 worldPosition;
-varying float area_enable_parallax;
-
+varying lowp vec4 varColor;
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
varying vec3 eyeVec;
-varying vec3 tsEyeVec;
-varying vec3 lightVec;
-varying vec3 tsLightVec;
-bool normalTexturePresent = false;
-
-const float e = 2.718281828459;
-const float BS = 10.0;
const float fogStart = FOG_START;
-const float fogShadingParameter = 1 / ( 1 - fogStart);
+const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
-#ifdef ENABLE_TONE_MAPPING
+#if ENABLE_TONE_MAPPING
/* Hable's UC2 Tone mapping parameters
A = 0.22;
@@ -55,7 +50,7 @@ vec4 applyToneMapping(vec4 color)
const float gamma = 1.6;
const float exposureBias = 5.5;
color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
- // Precalculated white_scale from
+ // Precalculated white_scale from
//vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
vec3 whiteScale = vec3(1.036015346);
color.rgb *= whiteScale;
@@ -63,150 +58,26 @@ vec4 applyToneMapping(vec4 color)
}
#endif
-void get_texture_flags()
-{
- vec4 flags = texture2D(textureFlags, vec2(0.0, 0.0));
- if (flags.r > 0.5) {
- normalTexturePresent = true;
- }
-}
-
-float intensity(vec3 color)
-{
- return (color.r + color.g + color.b) / 3.0;
-}
-
-float get_rgb_height(vec2 uv)
-{
- return intensity(texture2D(baseTexture, uv).rgb);
-}
-
-vec4 get_normal_map(vec2 uv)
-{
- vec4 bump = texture2D(normalTexture, uv).rgba;
- bump.xyz = normalize(bump.xyz * 2.0 - 1.0);
- return bump;
-}
-
-float find_intersection(vec2 dp, vec2 ds)
-{
- float depth = 1.0;
- float best_depth = 0.0;
- float size = 0.0625;
- for (int i = 0; i < 15; i++) {
- depth -= size;
- float h = texture2D(normalTexture, dp + ds * depth).a;
- if (depth <= h) {
- best_depth = depth;
- break;
- }
- }
- depth = best_depth;
- for (int i = 0; i < 4; i++) {
- size *= 0.5;
- float h = texture2D(normalTexture,dp + ds * depth).a;
- if (depth <= h) {
- best_depth = depth;
- depth += size;
- } else {
- depth -= size;
- }
- }
- return best_depth;
-}
-
-float find_intersectionRGB(vec2 dp, vec2 ds)
-{
- const float depth_step = 1.0 / 24.0;
- float depth = 1.0;
- for (int i = 0 ; i < 24 ; i++) {
- float h = get_rgb_height(dp + ds * depth);
- if (h >= depth)
- break;
- depth -= depth_step;
- }
- return depth;
-}
-
void main(void)
{
vec3 color;
- vec4 bump;
- vec2 uv = gl_TexCoord[0].st;
- bool use_normalmap = false;
- get_texture_flags();
-
-#ifdef ENABLE_PARALLAX_OCCLUSION
- vec2 eyeRay = vec2 (tsEyeVec.x, -tsEyeVec.y);
- const float scale = PARALLAX_OCCLUSION_SCALE / PARALLAX_OCCLUSION_ITERATIONS;
- const float bias = PARALLAX_OCCLUSION_BIAS / PARALLAX_OCCLUSION_ITERATIONS;
+ vec2 uv = varTexCoord.st;
-#if PARALLAX_OCCLUSION_MODE == 0
- // Parallax occlusion with slope information
- if (normalTexturePresent && area_enable_parallax > 0.0) {
- for (int i = 0; i < PARALLAX_OCCLUSION_ITERATIONS; i++) {
- vec4 normal = texture2D(normalTexture, uv.xy);
- float h = normal.a * scale - bias;
- uv += h * normal.z * eyeRay;
- }
-#endif
-
-#if PARALLAX_OCCLUSION_MODE == 1
- // Relief mapping
- if (normalTexturePresent && area_enable_parallax > 0.0) {
- vec2 ds = eyeRay * PARALLAX_OCCLUSION_SCALE;
- float dist = find_intersection(uv, ds);
- uv += dist * ds;
-#endif
- } else if (GENERATE_NORMALMAPS == 1 && area_enable_parallax > 0.0) {
- vec2 ds = eyeRay * PARALLAX_OCCLUSION_SCALE;
- float dist = find_intersectionRGB(uv, ds);
- uv += dist * ds;
- }
-#endif
-
-#if USE_NORMALMAPS == 1
- if (normalTexturePresent) {
- bump = get_normal_map(uv);
- use_normalmap = true;
- }
-#endif
-
-#if GENERATE_NORMALMAPS == 1
- if (normalTexturePresent == false) {
- float tl = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y + SAMPLE_STEP));
- float t = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y - SAMPLE_STEP));
- float tr = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y + SAMPLE_STEP));
- float r = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y));
- float br = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y - SAMPLE_STEP));
- float b = get_rgb_height(vec2(uv.x, uv.y - SAMPLE_STEP));
- float bl = get_rgb_height(vec2(uv.x -SAMPLE_STEP, uv.y - SAMPLE_STEP));
- float l = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y));
- float dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl);
- float dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr);
- bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0);
- use_normalmap = true;
+ vec4 base = texture2D(baseTexture, uv).rgba;
+#ifdef USE_DISCARD
+ // If alpha is zero, we can just discard the pixel. This fixes transparency
+ // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
+ // and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
+ if (base.a == 0.0) {
+ discard;
}
#endif
- vec4 base = texture2D(baseTexture, uv).rgba;
-#ifdef ENABLE_BUMPMAPPING
- if (use_normalmap) {
- vec3 L = normalize(lightVec);
- vec3 E = normalize(eyeVec);
- float specular = pow(clamp(dot(reflect(L, bump.xyz), E), 0.0, 1.0), 1.0);
- float diffuse = dot(-E,bump.xyz);
- color = (diffuse + 0.1 * specular) * base.rgb;
- } else {
- color = base.rgb;
- }
-#else
color = base.rgb;
-#endif
- vec4 col = vec4(color.rgb * gl_Color.rgb, 1.0);
-
-#ifdef ENABLE_TONE_MAPPING
+ vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
+
+#if ENABLE_TONE_MAPPING
col = applyToneMapping(col);
#endif
diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl
index 0d8d0a2a5..c68df4a8e 100644
--- a/client/shaders/nodes_shader/opengl_vertex.glsl
+++ b/client/shaders/nodes_shader/opengl_vertex.glsl
@@ -1,4 +1,3 @@
-uniform mat4 mWorldViewProj;
uniform mat4 mWorld;
// Color of the light emitted by the sun.
@@ -16,12 +15,16 @@ varying vec3 vPosition;
// cameraOffset + worldPosition (for large coordinates the limits of float
// precision must be considered).
varying vec3 worldPosition;
-
+varying lowp vec4 varColor;
+// The centroid keyword ensures that after interpolation the texture coordinates
+// lie within the same bounds when MSAA is en- and disabled.
+// This fixes the stripes problem with nearest-neighbour textures and MSAA.
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
varying vec3 eyeVec;
-varying vec3 lightVec;
-varying vec3 tsEyeVec;
-varying vec3 tsLightVec;
-varying float area_enable_parallax;
// Color of the light emitted by the light sources.
const vec3 artificialLight = vec3(1.04, 1.04, 1.04);
@@ -85,25 +88,13 @@ float snoise(vec3 p)
void main(void)
{
- gl_TexCoord[0] = gl_MultiTexCoord0;
- //TODO: make offset depending on view angle and parallax uv displacement
- //thats for textures that doesnt align vertically, like dirt with grass
- //gl_TexCoord[0].y += 0.008;
-
- //Allow parallax/relief mapping only for certain kind of nodes
- //Variable is also used to control area of the effect
-#if (DRAW_TYPE == NDT_NORMAL || DRAW_TYPE == NDT_LIQUID || DRAW_TYPE == NDT_FLOWINGLIQUID)
- area_enable_parallax = 1.0;
-#else
- area_enable_parallax = 0.0;
-#endif
-
+ varTexCoord = inTexCoord0.st;
-float disp_x;
-float disp_z;
+ float disp_x;
+ float disp_z;
// OpenGL < 4.3 does not support continued preprocessor lines
#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES) || (MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS)
- vec4 pos2 = mWorld * gl_Vertex;
+ vec4 pos2 = mWorld * inVertexPosition;
float tOffset = (pos2.x + pos2.y) * 0.001 + pos2.z * 0.002;
disp_x = (smoothTriangleWave(animationTimer * 23.0 + tOffset) +
smoothTriangleWave(animationTimer * 11.0 + tOffset)) * 0.4;
@@ -112,68 +103,43 @@ float disp_z;
smoothTriangleWave(animationTimer * 13.0 + tOffset)) * 0.5;
#endif
- worldPosition = (mWorld * gl_Vertex).xyz;
+ worldPosition = (mWorld * inVertexPosition).xyz;
// OpenGL < 4.3 does not support continued preprocessor lines
#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC) && ENABLE_WAVING_WATER
// Generate waves with Perlin-type noise.
// The constants are calibrated such that they roughly
// correspond to the old sine waves.
- vec4 pos = gl_Vertex;
+ vec4 pos = inVertexPosition;
vec3 wavePos = worldPosition + cameraOffset;
// The waves are slightly compressed along the z-axis to get
// wave-fronts along the x-axis.
- wavePos.x /= WATER_WAVE_LENGTH * 3;
- wavePos.z /= WATER_WAVE_LENGTH * 2;
- wavePos.z += animationTimer * WATER_WAVE_SPEED * 10;
- pos.y += (snoise(wavePos) - 1) * WATER_WAVE_HEIGHT * 5;
+ wavePos.x /= WATER_WAVE_LENGTH * 3.0;
+ wavePos.z /= WATER_WAVE_LENGTH * 2.0;
+ wavePos.z += animationTimer * WATER_WAVE_SPEED * 10.0;
+ pos.y += (snoise(wavePos) - 1.0) * WATER_WAVE_HEIGHT * 5.0;
gl_Position = mWorldViewProj * pos;
#elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES
- vec4 pos = gl_Vertex;
+ vec4 pos = inVertexPosition;
pos.x += disp_x;
pos.y += disp_z * 0.1;
pos.z += disp_z;
gl_Position = mWorldViewProj * pos;
#elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS
- vec4 pos = gl_Vertex;
- if (gl_TexCoord[0].y < 0.05) {
+ vec4 pos = inVertexPosition;
+ if (varTexCoord.y < 0.05) {
pos.x += disp_x;
pos.z += disp_z;
}
gl_Position = mWorldViewProj * pos;
#else
- gl_Position = mWorldViewProj * gl_Vertex;
+ gl_Position = mWorldViewProj * inVertexPosition;
#endif
vPosition = gl_Position.xyz;
- // Don't generate heightmaps when too far from the eye
- float dist = distance (vec3(0.0, 0.0, 0.0), vPosition);
- if (dist > 150.0) {
- area_enable_parallax = 0.0;
- }
-
- vec3 sunPosition = vec3 (0.0, eyePosition.y * BS + 900.0, 0.0);
-
- vec3 normal, tangent, binormal;
- normal = normalize(gl_NormalMatrix * gl_Normal);
- tangent = normalize(gl_NormalMatrix * gl_MultiTexCoord1.xyz);
- binormal = normalize(gl_NormalMatrix * gl_MultiTexCoord2.xyz);
-
- vec3 v;
-
- lightVec = sunPosition - worldPosition;
- v.x = dot(lightVec, tangent);
- v.y = dot(lightVec, binormal);
- v.z = dot(lightVec, normal);
- tsLightVec = normalize (v);
-
- eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz;
- v.x = dot(eyeVec, tangent);
- v.y = dot(eyeVec, binormal);
- v.z = dot(eyeVec, normal);
- tsEyeVec = normalize (v);
+ eyeVec = -(mWorldView * inVertexPosition).xyz;
// Calculate color.
// Red, green and blue components are pre-multiplied with
@@ -182,16 +148,16 @@ float disp_z;
// The pre-baked colors are halved to prevent overflow.
vec4 color;
// The alpha gives the ratio of sunlight in the incoming light.
- float nightRatio = 1 - gl_Color.a;
- color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb +
- nightRatio * artificialLight.rgb) * 2;
- color.a = 1;
+ float nightRatio = 1.0 - inVertexColor.a;
+ color.rgb = inVertexColor.rgb * (inVertexColor.a * dayLight.rgb +
+ nightRatio * artificialLight.rgb) * 2.0;
+ color.a = 1.0;
// Emphase blue a bit in darker places
// See C++ implementation in mapblock_mesh.cpp final_color_blend()
- float brightness = (color.r + color.g + color.b) / 3;
+ float brightness = (color.r + color.g + color.b) / 3.0;
color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) +
0.07 * brightness);
- gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0);
+ varColor = clamp(color, 0.0, 1.0);
}
diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl
index 0534dc049..9a81d8185 100644
--- a/client/shaders/object_shader/opengl_fragment.glsl
+++ b/client/shaders/object_shader/opengl_fragment.glsl
@@ -1,6 +1,4 @@
uniform sampler2D baseTexture;
-uniform sampler2D normalTexture;
-uniform sampler2D textureFlags;
uniform vec4 emissiveColor;
uniform vec4 skyBgColor;
@@ -10,22 +8,22 @@ uniform vec3 eyePosition;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 worldPosition;
+varying lowp vec4 varColor;
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
varying vec3 eyeVec;
-varying vec3 lightVec;
varying float vIDiff;
-bool normalTexturePresent = false;
-bool texTileableHorizontal = false;
-bool texTileableVertical = false;
-bool texSeamless = false;
-
const float e = 2.718281828459;
const float BS = 10.0;
const float fogStart = FOG_START;
-const float fogShadingParameter = 1 / ( 1 - fogStart);
+const float fogShadingParameter = 1.0 / (1.0 - fogStart);
-#ifdef ENABLE_TONE_MAPPING
+#if ENABLE_TONE_MAPPING
/* Hable's UC2 Tone mapping parameters
A = 0.22;
@@ -49,7 +47,7 @@ vec4 applyToneMapping(vec4 color)
const float gamma = 1.6;
const float exposureBias = 5.5;
color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
- // Precalculated white_scale from
+ // Precalculated white_scale from
//vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
vec3 whiteScale = vec3(1.036015346);
color.rgb *= whiteScale;
@@ -57,99 +55,31 @@ vec4 applyToneMapping(vec4 color)
}
#endif
-void get_texture_flags()
-{
- vec4 flags = texture2D(textureFlags, vec2(0.0, 0.0));
- if (flags.r > 0.5) {
- normalTexturePresent = true;
- }
- if (flags.g > 0.5) {
- texTileableHorizontal = true;
- }
- if (flags.b > 0.5) {
- texTileableVertical = true;
- }
- if (texTileableHorizontal && texTileableVertical) {
- texSeamless = true;
- }
-}
-
-float intensity(vec3 color)
-{
- return (color.r + color.g + color.b) / 3.0;
-}
-
-float get_rgb_height(vec2 uv)
-{
- if (texSeamless) {
- return intensity(texture2D(baseTexture, uv).rgb);
- } else {
- return intensity(texture2D(baseTexture, clamp(uv, 0.0, 0.999)).rgb);
- }
-}
-
-vec4 get_normal_map(vec2 uv)
-{
- vec4 bump = texture2D(normalTexture, uv).rgba;
- bump.xyz = normalize(bump.xyz * 2.0 - 1.0);
- return bump;
-}
-
void main(void)
{
vec3 color;
- vec4 bump;
- vec2 uv = gl_TexCoord[0].st;
- bool use_normalmap = false;
- get_texture_flags();
-
-#if USE_NORMALMAPS == 1
- if (normalTexturePresent) {
- bump = get_normal_map(uv);
- use_normalmap = true;
- }
-#endif
-
-#if GENERATE_NORMALMAPS == 1
- if (normalTexturePresent == false) {
- float tl = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y + SAMPLE_STEP));
- float t = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y - SAMPLE_STEP));
- float tr = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y + SAMPLE_STEP));
- float r = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y));
- float br = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y - SAMPLE_STEP));
- float b = get_rgb_height(vec2(uv.x, uv.y - SAMPLE_STEP));
- float bl = get_rgb_height(vec2(uv.x -SAMPLE_STEP, uv.y - SAMPLE_STEP));
- float l = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y));
- float dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl);
- float dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr);
- bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0);
- use_normalmap = true;
- }
-#endif
+ vec2 uv = varTexCoord.st;
vec4 base = texture2D(baseTexture, uv).rgba;
-#ifdef ENABLE_BUMPMAPPING
- if (use_normalmap) {
- vec3 L = normalize(lightVec);
- vec3 E = normalize(eyeVec);
- float specular = pow(clamp(dot(reflect(L, bump.xyz), E), 0.0, 1.0), 1.0);
- float diffuse = dot(-E,bump.xyz);
- color = (diffuse + 0.1 * specular) * base.rgb;
- } else {
- color = base.rgb;
+#ifdef USE_DISCARD
+ // If alpha is zero, we can just discard the pixel. This fixes transparency
+ // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
+ // and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
+ if (base.a == 0.0) {
+ discard;
}
-#else
- color = base.rgb;
#endif
+ color = base.rgb;
+
vec4 col = vec4(color.rgb, base.a);
- col.rgb *= gl_Color.rgb;
+ col.rgb *= varColor.rgb;
col.rgb *= emissiveColor.rgb * vIDiff;
-#ifdef ENABLE_TONE_MAPPING
+#if ENABLE_TONE_MAPPING
col = applyToneMapping(col);
#endif
diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl
index 968a07e22..b4a4d0aaa 100644
--- a/client/shaders/object_shader/opengl_vertex.glsl
+++ b/client/shaders/object_shader/opengl_vertex.glsl
@@ -1,4 +1,3 @@
-uniform mat4 mWorldViewProj;
uniform mat4 mWorld;
uniform vec3 eyePosition;
@@ -7,9 +6,14 @@ uniform float animationTimer;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec3 worldPosition;
+varying lowp vec4 varColor;
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
varying vec3 eyeVec;
-varying vec3 lightVec;
varying float vIDiff;
const float e = 2.718281828459;
@@ -19,35 +23,31 @@ float directional_ambient(vec3 normal)
{
vec3 v = normal * normal;
- if (normal.y < 0)
- return dot(v, vec3(0.670820f, 0.447213f, 0.836660f));
+ if (normal.y < 0.0)
+ return dot(v, vec3(0.670820, 0.447213, 0.836660));
- return dot(v, vec3(0.670820f, 1.000000f, 0.836660f));
+ return dot(v, vec3(0.670820, 1.000000, 0.836660));
}
void main(void)
{
- gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
- gl_Position = mWorldViewProj * gl_Vertex;
+ varTexCoord = (mTexture * inTexCoord0).st;
+ gl_Position = mWorldViewProj * inVertexPosition;
vPosition = gl_Position.xyz;
- vNormal = gl_Normal;
- worldPosition = (mWorld * gl_Vertex).xyz;
-
- vec3 sunPosition = vec3 (0.0, eyePosition.y * BS + 900.0, 0.0);
-
- lightVec = sunPosition - worldPosition;
- eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz;
+ vNormal = inVertexNormal;
+ worldPosition = (mWorld * inVertexPosition).xyz;
+ eyeVec = -(mWorldView * inVertexPosition).xyz;
#if (MATERIAL_TYPE == TILE_MATERIAL_PLAIN) || (MATERIAL_TYPE == TILE_MATERIAL_PLAIN_ALPHA)
vIDiff = 1.0;
#else
// This is intentional comparison with zero without any margin.
// If normal is not equal to zero exactly, then we assume it's a valid, just not normalized vector
- vIDiff = length(gl_Normal) == 0.0
+ vIDiff = length(inVertexNormal) == 0.0
? 1.0
- : directional_ambient(normalize(gl_Normal));
+ : directional_ambient(normalize(inVertexNormal));
#endif
- gl_FrontColor = gl_BackColor = gl_Color;
+ varColor = inVertexColor;
}
diff --git a/client/shaders/selection_shader/opengl_fragment.glsl b/client/shaders/selection_shader/opengl_fragment.glsl
index c679d0e12..35b1f8902 100644
--- a/client/shaders/selection_shader/opengl_fragment.glsl
+++ b/client/shaders/selection_shader/opengl_fragment.glsl
@@ -1,9 +1,12 @@
uniform sampler2D baseTexture;
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
+
void main(void)
{
- vec2 uv = gl_TexCoord[0].st;
+ vec2 uv = varTexCoord.st;
vec4 color = texture2D(baseTexture, uv);
- color.rgb *= gl_Color.rgb;
+ color.rgb *= varColor.rgb;
gl_FragColor = color;
}
diff --git a/client/shaders/selection_shader/opengl_vertex.glsl b/client/shaders/selection_shader/opengl_vertex.glsl
index d0b16c8b0..9ca87a9cf 100644
--- a/client/shaders/selection_shader/opengl_vertex.glsl
+++ b/client/shaders/selection_shader/opengl_vertex.glsl
@@ -1,9 +1,10 @@
-uniform mat4 mWorldViewProj;
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
void main(void)
{
- gl_TexCoord[0] = gl_MultiTexCoord0;
- gl_Position = mWorldViewProj * gl_Vertex;
+ varTexCoord = inTexCoord0.st;
+ gl_Position = mWorldViewProj * inVertexPosition;
- gl_FrontColor = gl_BackColor = gl_Color;
+ varColor = inVertexColor;
}
diff --git a/client/shaders/stars_shader/opengl_fragment.glsl b/client/shaders/stars_shader/opengl_fragment.glsl
new file mode 100644
index 000000000..a9ed741bf
--- /dev/null
+++ b/client/shaders/stars_shader/opengl_fragment.glsl
@@ -0,0 +1,6 @@
+uniform vec4 starColor;
+
+void main(void)
+{
+ gl_FragColor = starColor;
+}
diff --git a/client/shaders/stars_shader/opengl_vertex.glsl b/client/shaders/stars_shader/opengl_vertex.glsl
new file mode 100644
index 000000000..77c401f34
--- /dev/null
+++ b/client/shaders/stars_shader/opengl_vertex.glsl
@@ -0,0 +1,4 @@
+void main(void)
+{
+ gl_Position = mWorldViewProj * inVertexPosition;
+}
diff --git a/clientmods/preview/init.lua b/clientmods/preview/init.lua
index 089955d2f..977ed0ec3 100644
--- a/clientmods/preview/init.lua
+++ b/clientmods/preview/init.lua
@@ -109,6 +109,10 @@ core.register_on_sending_chat_message(function(message)
return false
end)
+core.register_on_chatcommand(function(command, params)
+ print("[PREVIEW] caught command '"..command.."'. Parameters: '"..params.."'")
+end)
+
-- This is an example function to ensure it's working properly, should be removed before merge
core.register_on_hp_modification(function(hp)
print("[PREVIEW] HP modified " .. hp)
diff --git a/doc/builtin_entities.txt b/doc/builtin_entities.txt
new file mode 100644
index 000000000..be3f73357
--- /dev/null
+++ b/doc/builtin_entities.txt
@@ -0,0 +1,101 @@
+# Builtin Entities
+Minetest registers two entities by default: Falling nodes and dropped items.
+This document describes how they behave and what you can do with them.
+
+## Falling node (`__builtin:falling_node`)
+
+This entity is created by `minetest.check_for_falling` in place of a node
+with the special group `falling_node=1`. Falling nodes can also be created
+artificially with `minetest.spawn_falling_node`.
+
+Needs manual initialization when spawned using `/spawnentity`.
+
+Default behaviour:
+
+* Falls down in a straight line (gravity = `movement_gravity` setting)
+* Collides with `walkable` node
+* Collides with all physical objects except players
+* If the node group `float=1` is set, it also collides with liquid nodes
+* When it hits a solid (=`walkable`) node, it will try to place itself as a
+ node, replacing the node above.
+ * If the falling node cannot replace the destination node, it is dropped.
+ * If the destination node is a leveled node (`paramtype2="leveled"`) of the
+ same node name, the levels of both are summed.
+
+### Entity fields
+
+* `set_node(self, node[, meta])`
+ * Function to initialize the falling node
+ * `node` and `meta` are explained below.
+ * The `meta` argument is optional.
+* `node`: Node table of the node (`name`, `param1`, `param2`) that this
+ entity represents. Read-only.
+* `meta`: Node metadata of the falling node. Will be used when the falling
+ nodes tries to place itself as a node. Read-only.
+
+### Rendering / supported nodes
+
+Falling nodes have visuals to look as close as possible to the original node.
+This works for most drawtypes, but there are limitations.
+
+Supported drawtypes:
+
+* `normal`
+* `signlike`
+* `torchlike`
+* `nodebox`
+* `raillike`
+* `glasslike`
+* `glasslike_framed`
+* `glasslike_framed_optional`
+* `allfaces`
+* `allfaces_optional`
+* `firelike`
+* `mesh`
+* `fencelike`
+* `liquid`
+* `airlike` (not pointable)
+
+Other drawtypes still kinda work, but they might look weird.
+
+Supported `paramtype2` values:
+
+* `wallmounted`
+* `facedir`
+* `colorwallmounted`
+* `colorfacedir`
+* `color`
+
+## Dropped item stack (`__builtin:item`)
+
+This is an item stack in a collectable form.
+
+Common cases that spawn a dropped item:
+
+* Item dropped by player
+* The root node of a node with the group `attached_node=1` is removed
+* `minetest.add_item` is called
+
+Needs manual initialization when spawned using `/spawnentity`.
+
+### Behavior
+
+* Players can collect it by punching
+* Lifespan is defined by the setting `item_entity_ttl`
+* Slides on `slippery` nodes
+* Subject to gravity (uses `movement_gravity` setting)
+* Collides with `walkable` nodes
+* Does not collide physical objects
+* When it's inside a solid (`walkable=true`) node, it tries to escape to a
+ neighboring non-solid (`walkable=false`) node
+
+### Entity fields
+
+* `set_item(self, item)`:
+ * Function to initialize the dropped item
+ * `item` (type `ItemStack`) specifies the item to represent
+* `age`: Age in seconds. Behaviour according to the setting `item_entity_ttl`
+* `itemstring`: Itemstring of the item that this item entity represents.
+ Read-only.
+
+Other fields are for internal use only.
diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt
index c9cd8ac93..098596481 100644
--- a/doc/client_lua_api.txt
+++ b/doc/client_lua_api.txt
@@ -1,4 +1,4 @@
-Minetest Lua Client Modding API Reference 5.3.0
+Minetest Lua Client Modding API Reference 5.4.0
================================================
* More information at <http://www.minetest.net/>
* Developer Wiki: <http://dev.minetest.net/>
@@ -620,7 +620,7 @@ Helper functions
* `minetest.is_yes(arg)`
* returns whether `arg` can be interpreted as yes
* `minetest.is_nan(arg)`
- * returns true true when the passed number represents NaN.
+ * returns true when the passed number represents NaN.
* `table.copy(table)`: returns a table
* returns a deep copy of `table`
@@ -686,6 +686,11 @@ Call these functions only at load time!
* Adds definition to minetest.registered_chatcommands
* `minetest.unregister_chatcommand(name)`
* Unregisters a chatcommands registered with register_chatcommand.
+* `minetest.register_on_chatcommand(function(command, params))`
+ * Called always when a chatcommand is triggered, before `minetest.registered_chatcommands`
+ is checked to see if that the command exists, but after the input is parsed.
+ * Return `true` to mark the command as handled, which means that the default
+ handlers will be prevented.
* `minetest.register_on_death(function())`
* Called when the local player dies
* `minetest.register_on_hp_modification(function(hp))`
@@ -768,12 +773,15 @@ Call these functions only at load time!
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* `search_center` is an optional boolean (default: `false`)
If true `pos` is also checked for the nodes
-* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of
- positions.
+* `minetest.find_nodes_in_area(pos1, pos2, nodenames, [grouped])`
+ * `pos1` and `pos2` are the min and max positions of the area to search.
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
- * First return value: Table with all node positions
- * Second return value: Table with the count of each node with the node name
- as index.
+ * If `grouped` is true the return value is a table indexed by node name
+ which contains lists of positions.
+ * If `grouped` is false or absent the return values are as follows:
+ first value: Table with all node positions
+ second value: Table with the count of each node with the node name
+ as index
* Area volume is limited to 4,096,000 nodes
* `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a
list of positions.
@@ -993,6 +1001,7 @@ Please do not try to access the reference until the camera is initialized, other
### LocalPlayer
An interface to retrieve information about the player.
+This object will only be available after the client is initialized. Earlier accesses will yield a `nil` value.
Methods:
@@ -1097,8 +1106,8 @@ Methods:
aux1 = boolean,
sneak = boolean,
zoom = boolean,
- LMB = boolean,
- RMB = boolean,
+ dig = boolean,
+ place = boolean,
}
```
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index f2a83eca5..d3165b9fd 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -62,12 +62,12 @@ Where `<gameid>` is unique to each game.
The game directory can contain the following files:
* `game.conf`, with the following keys:
- * `name`: Required, human readable name e.g. `name = Minetest`
+ * `name`: Required, a human readable title to address the game, e.g. `name = Minetest`.
* `description`: Short description to be shown in the content tab
* `allowed_mapgens = <comma-separated mapgens>`
e.g. `allowed_mapgens = v5,v6,flat`
- Mapgens not in this list are removed from the list of mapgens for
- the game.
+ Mapgens not in this list are removed from the list of mapgens for the
+ game.
If not specified, all mapgens are allowed.
* `disallowed_mapgens = <comma-separated mapgens>`
e.g. `disallowed_mapgens = v5,v6,flat`
@@ -79,6 +79,10 @@ The game directory can contain the following files:
e.g. `disallowed_mapgen_settings = mgv5_spflags`
These settings are hidden for this game in the world creation
dialog and game start menu.
+ * `author`: The author of the game. It only appears when downloaded from
+ ContentDB.
+ * `release`: Ignore this: Should only ever be set by ContentDB, as it is
+ an internal ID used to track versions.
* `minetest.conf`:
Used to set default settings when running this game.
* `settingtypes.txt`:
@@ -134,9 +138,15 @@ Mods can be put in a subdirectory, if the parent directory, which otherwise
should be a mod, contains a file named `modpack.conf`.
The file is a key-value store of modpack details.
-* `name`: The modpack name.
+* `name`: The modpack name. Allows Minetest to determine the modpack name even
+ if the folder is wrongly named.
* `description`: Description of mod to be shown in the Mods tab of the main
menu.
+* `author`: The author of the modpack. It only appears when downloaded from
+ ContentDB.
+* `release`: Ignore this: Should only ever be set by ContentDB, as it is an
+ internal ID used to track versions.
+* `title`: A human-readable title to address the modpack.
Note: to support 0.4.x, please also create an empty modpack.txt file.
@@ -152,7 +162,12 @@ Mod directory structure
│   ├── models
│   ├── textures
│   │   ├── modname_stuff.png
- │   │   └── modname_something_else.png
+ │   │   ├── modname_stuff_normal.png
+ │   │   ├── modname_something_else.png
+ │   │   ├── subfolder_foo
+ │   │   │ ├── modname_more_stuff.png
+ │   │   │ └── another_subfolder
+ │   │   └── bar_subfolder
│   ├── sounds
│   ├── media
│   ├── locale
@@ -176,6 +191,11 @@ A `Settings` file that provides meta information about the mod.
loaded before this mod.
* `optional_depends`: A comma separated list of optional dependencies.
Like a dependency, but no error if the mod doesn't exist.
+* `author`: The author of the mod. It only appears when downloaded from
+ ContentDB.
+* `release`: Ignore this: Should only ever be set by ContentDB, as it is an
+ internal ID used to track versions.
+* `title`: A human-readable title to address the mod.
Note: to support 0.4.x, please also provide depends.txt.
@@ -221,18 +241,23 @@ registered callbacks.
`minetest.settings` can be used to read custom or existing settings at load
time, if necessary. (See [`Settings`])
-### `models`
-
-Models for entities or meshnodes.
-
-### `textures`, `sounds`, `media`
+### `textures`, `sounds`, `media`, `models`, `locale`
Media files (textures, sounds, whatever) that will be transferred to the
-client and will be available for use by the mod.
+client and will be available for use by the mod and translation files for
+the clients (see [Translations]).
+
+It is suggested to use the folders for the purpous they are thought for,
+eg. put textures into `textures`, translation files into `locale`,
+models for entities or meshnodes into `models` et cetera.
-### `locale`
+These folders and subfolders can contain subfolders.
+Subfolders with names starting with `_` or `.` are ignored.
+If a subfolder contains a media file with the same name as a media file
+in one of its parents, the parent's file is used.
-Translation files for the clients. (See [Translations])
+Although it is discouraged, a mod can overwrite a media file of any mod that it
+depends on by supplying a file with an equal name.
Naming conventions
------------------
@@ -378,11 +403,14 @@ stripping out the file extension:
* e.g. `foomod_foothing.png`
* e.g. `foomod_foothing`
+
Texture modifiers
-----------------
There are various texture modifiers that can be used
-to generate textures on-the-fly.
+to let the client generate textures on-the-fly.
+The modifiers are applied directly in sRGB colorspace,
+i.e. without gamma-correction.
### Texture overlaying
@@ -780,7 +808,7 @@ Example (colored grass block):
-- Overlay tiles: define them in the same style
-- The top and bottom tile does not have overlay
overlay_tiles = {"", "",
- {name = "default_grass_side.png", tileable_vertical = false}},
+ {name = "default_grass_side.png"}},
-- Global color, used in inventory
color = "green",
-- Palette in the world
@@ -989,7 +1017,9 @@ The function of `param2` is determined by `paramtype2` in node definition.
* `paramtype2 = "flowingliquid"`
* Used by `drawtype = "flowingliquid"` and `liquidtype = "flowing"`
* The liquid level and a flag of the liquid are stored in `param2`
- * Bits 0-2: Liquid level (0-7). The higher, the more liquid is in this node
+ * Bits 0-2: Liquid level (0-7). The higher, the more liquid is in this node;
+ see `minetest.get_node_level`, `minetest.set_node_level` and `minetest.add_node_level`
+ to access/manipulate the content of this field
* Bit 3: If set, liquid is flowing downwards (no graphical effect)
* `paramtype2 = "wallmounted"`
* Supported drawtypes: "torchlike", "signlike", "normal", "nodebox", "mesh"
@@ -1150,7 +1180,7 @@ Look for examples in `games/devtest` or `games/minetest_game`.
base cube without affecting them.
* The base cube texture tiles are defined as normal, the `plantlike`
extension uses the defined special tile, for example:
- `special_tiles = {{name = "default_papyrus.png", tileable_vertical = true}},`
+ `special_tiles = {{name = "default_papyrus.png"}},`
`*_optional` drawtypes need less rendering time if deactivated
(always client-side).
@@ -1220,6 +1250,9 @@ A box of a regular node would look like:
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
+To avoid collision issues, keep each value within the range of +/- 1.45.
+This also applies to leveled nodeboxes, where the final height shall not
+exceed this soft limit.
@@ -1414,7 +1447,32 @@ Same as `image`, but does not accept a `position`; the position is instead deter
* `world_pos`: World position of the waypoint.
* `offset`: offset in pixels from position.
+### `compass`
+
+Displays an image oriented or translated according to current heading direction.
+
+* `size`: The size of this element. Negative values represent percentage
+ of the screen; e.g. `x=-100` means 100% (width).
+* `scale`: Scale of the translated image (used only for dir = 2 or dir = 3).
+* `text`: The name of the texture to use.
+* `alignment`: The alignment of the image.
+* `offset`: Offset in pixels from position.
+* `dir`: How the image is rotated/translated:
+ * 0 - Rotate as heading direction
+ * 1 - Rotate in reverse direction
+ * 2 - Translate as landscape direction
+ * 3 - Translate in reverse direction
+
+If translation is chosen, texture is repeated horizontally to fill the whole element.
+### `minimap`
+
+Displays a minimap on the HUD.
+
+* `size`: Size of the minimap to display. Minimap should be a square to avoid
+ distortion.
+* `alignment`: The alignment of the minimap.
+* `offset`: offset in pixels from position.
Representations of simple things
================================
@@ -1690,8 +1748,9 @@ to games.
### `ObjectRef` groups
* `immortal`: Skips all damage and breath handling for an object. This group
- will also hide the integrated HUD status bars for players, and is
- automatically set to all players when damage is disabled on the server.
+ will also hide the integrated HUD status bars for players. It is
+ automatically set to all players when damage is disabled on the server and
+ cannot be reset (subject to change).
* `punch_operable`: For entities; disables the regular damage mechanism for
players punching it by hand or a non-tool item, so that it can do something
else than take damage.
@@ -1973,8 +2032,10 @@ Item metadata only contains a key-value store.
Some of the values in the key-value store are handled specially:
-* `description`: Set the item stack's description. Defaults to
- `idef.description`.
+* `description`: Set the item stack's description.
+ See also: `get_description` in [`ItemStack`]
+* `short_description`: Set the item stack's short description.
+ See also: `get_short_description` in [`ItemStack`]
* `color`: A `ColorString`, which sets the stack's color.
* `palette_index`: If the item has a palette, this is used to get the
current color from the palette.
@@ -2057,6 +2118,22 @@ Examples
list[current_player;craft;3,0;3,3;]
list[current_player;craftpreview;7,1;1,1;]
+Version History
+---------------
+
+* FORMSPEC VERSION 1:
+ * (too much)
+* FORMSPEC VERSION 2:
+ * Forced real coordinates
+ * background9[]: 9-slice scaling parameters
+* FORMSPEC VERSION 3:
+ * Formspec elements are drawn in the order of definition
+ * bgcolor[]: use 3 parameters (bgcolor, formspec (now an enum), fbgcolor)
+ * box[] and image[] elements enable clipping by default
+ * new element: scroll_container[]
+* FORMSPEC VERSION 4:
+ * Allow dropdown indexing events
+
Elements
--------
@@ -2068,6 +2145,7 @@ Elements
* Clients older than this version can neither show newer elements nor display
elements with new arguments correctly.
* Available since feature `formspec_version_element`.
+* See also: [Version History]
### `size[<W>,<H>,<fixed_size>]`
@@ -2152,7 +2230,8 @@ Elements
* Show an inventory list if it has been sent to the client. Nothing will
be shown if the inventory list is of size 0.
* **Note**: With the new coordinate system, the spacing between inventory
- slots is one-fourth the size of an inventory slot.
+ slots is one-fourth the size of an inventory slot by default. Also see
+ [Styling Formspecs] for changing the size of slots and spacing.
### `list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]`
@@ -2220,6 +2299,21 @@ Elements
* `frame duration`: Milliseconds between each frame. `0` means the frames don't advance.
* `frame start` (Optional): The index of the frame to start on. Default `1`.
+### `model[<X>,<Y>;<W>,<H>;<name>;<mesh>;<textures>;<rotation X,Y>;<continuous>;<mouse control>;<frame loop range>]`
+
+* Show a mesh model.
+* `name`: Element name that can be used for styling
+* `mesh`: The mesh model to use.
+* `textures`: The mesh textures to use according to the mesh materials.
+ Texture names must be separated by commas.
+* `rotation {X,Y}` (Optional): Initial rotation of the camera.
+ The axes are euler angles in degrees.
+* `continuous` (Optional): Whether the rotation is continuous. Default `false`.
+* `mouse control` (Optional): Whether the model can be controlled with the mouse. Default `true`.
+* `frame loop range` (Optional): Range of the animation frames.
+ * Defaults to the full range of all available frames.
+ * Syntax: `<begin>,<end>`
+
### `item_image[<X>,<Y>;<W>,<H>;<item name>]`
* Show an inventory image of registered item/node
@@ -2442,8 +2536,10 @@ Elements
* Simple colored box
* `color` is color specified as a `ColorString`.
If the alpha component is left blank, the box will be semitransparent.
+ If the color is not specified, the box will use the options specified by
+ its style. If the color is specified, all styling options will be ignored.
-### `dropdown[<X>,<Y>;<W>;<name>;<item 1>,<item 2>, ...,<item n>;<selected idx>]`
+### `dropdown[<X>,<Y>;<W>;<name>;<item 1>,<item 2>, ...,<item n>;<selected idx>;<index event>]`
* Show a dropdown field
* **Important note**: There are two different operation modes:
@@ -2454,8 +2550,12 @@ Elements
* Fieldname data is transferred to Lua
* Items to be shown in dropdown
* Index of currently selected dropdown item
+* `index event` (optional, allowed parameter since formspec version 4): Specifies the
+ event field value for selected items.
+ * `true`: Selected item index
+ * `false` (default): Selected item value
-### `dropdown[<X>,<Y>;<W>,<H>;<name>;<item 1>,<item 2>, ...,<item n>;<selected idx>]`
+### `dropdown[<X>,<Y>;<W>,<H>;<name>;<item 1>,<item 2>, ...,<item n>;<selected idx>;<index event>]`
* Show a dropdown field
* **Important note**: This syntax for dropdowns can only be used with the
@@ -2468,6 +2568,10 @@ Elements
* Fieldname data is transferred to Lua
* Items to be shown in dropdown
* Index of currently selected dropdown item
+* `index event` (optional, allowed parameter since formspec version 4): Specifies the
+ event field value for selected items.
+ * `true`: Selected item index
+ * `false` (default): Selected item value
### `checkbox[<X>,<Y>;<name>;<label>;<selected>]`
@@ -2484,7 +2588,7 @@ Elements
* There are two ways to use it:
1. handle the changed event (only changed scrollbar is available)
2. read the value on pressing a button (all scrollbars are available)
-* `orientation`: `vertical`/`horizontal`
+* `orientation`: `vertical`/`horizontal`. Default horizontal.
* Fieldname data is transferred to Lua
* Value of this trackbar is set to (`0`-`1000`) by default
* See also `minetest.explode_scrollbar_event`
@@ -2598,6 +2702,28 @@ Elements
* All provided states must be active for the style to apply.
* See [Styling Formspecs].
+### `set_focus[<name>;<force>]`
+
+* Sets the focus to the element with the same `name` parameter.
+* **Note**: This element must be placed before the element it focuses.
+* `force` (optional, default `false`): By default, focus is not applied for
+ re-sent formspecs with the same name so that player-set focus is kept.
+ `true` sets the focus to the specified element for every sent formspec.
+* The following elements have the ability to be focused:
+ * checkbox
+ * button
+ * button_exit
+ * image_button
+ * image_button_exit
+ * item_image_button
+ * table
+ * textlist
+ * dropdown
+ * field
+ * pwdfield
+ * textarea
+ * scrollbar
+
Migrating to Real Coordinates
-----------------------------
@@ -2678,21 +2804,25 @@ Setting a property to nothing will reset it to the default value. For example:
Some types may inherit styles from parent types.
* animated_image, inherits from image
+* box
* button
* button_exit, inherits from button
* checkbox
-* scrollbar
-* table
-* textlist
* dropdown
* field
-* pwdfield, inherits from field
-* textarea
-* label
-* vertlabel, inherits from field
+* image
* image_button
* item_image_button
+* label
+* list
+* model
+* pwdfield, inherits from field
+* scrollbar
* tabheader
+* table
+* textarea
+* textlist
+* vertlabel, inherits from label
### Valid Properties
@@ -2701,7 +2831,18 @@ Some types may inherit styles from parent types.
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* box
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
- * Default to false in formspec_version version 3 or higher
+ * Defaults to false in formspec_version version 3 or higher
+ * **Note**: `colors`, `bordercolors`, and `borderwidths` accept multiple input types:
+ * Single value (e.g. `#FF0`): All corners/borders.
+ * Two values (e.g. `red,#FFAAFF`): top-left and bottom-right,top-right and bottom-left/
+ top and bottom,left and right.
+ * Four values (e.g. `blue,#A0F,green,#FFFA`): top-left/top and rotates clockwise.
+ * These work similarly to CSS borders.
+ * colors - `ColorString`. Sets the color(s) of the box corners. Default `black`.
+ * bordercolors - `ColorString`. Sets the color(s) of the borders. Default `black`.
+ * borderwidths - Integer. Sets the width(s) of the borders in pixels. If the width is
+ negative, the border will extend inside the box, whereas positive extends outside
+ the box. A width of zero results in no border; this is default.
* button, button_exit, image_button, item_image_button
* alpha - boolean, whether to draw alpha in bgimg. Default true.
* bgcolor - color, sets button tint.
@@ -2717,31 +2858,54 @@ Some types may inherit styles from parent types.
button's content when set.
* bgimg_pressed - background image when pressed. Defaults to bgimg when not provided.
* This is deprecated, use states instead.
+ * font - Sets font type. This is a comma separated list of options. Valid options:
+ * Main font type options. These cannot be combined with each other:
+ * `normal`: Default font
+ * `mono`: Monospaced font
+ * Font modification options. If used without a main font type, `normal` is used:
+ * `bold`: Makes font bold.
+ * `italic`: Makes font italic.
+ Default `normal`.
+ * font_size - Sets font size. Default is user-set. Can have multiple values:
+ * `<number>`: Sets absolute font size to `number`.
+ * `+<number>`/`-<number>`: Offsets default font size by `number` points.
+ * `*<number>`: Multiplies default font size by `number`, similar to CSS `em`.
* border - boolean, draw border. Set to false to hide the bevelled button pane. Default true.
* content_offset - 2d vector, shifts the position of the button's content without resizing it.
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* padding - rect, adds space between the edges of the button and the content. This value is
relative to bgimg_middle.
+ * sound - a sound to be played when triggered.
* textcolor - color, default white.
* checkbox
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
-* scrollbar
- * noclip - boolean, set to true to allow the element to exceed formspec bounds.
-* table, textlist
- * noclip - boolean, set to true to allow the element to exceed formspec bounds.
+ * sound - a sound to be played when triggered.
* dropdown
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
+ * sound - a sound to be played when the entry is changed.
* field, pwdfield, textarea
* border - set to false to hide the textbox background and border. Default true.
+ * font - Sets font type. See button `font` property for more information.
+ * font_size - Sets font size. See button `font_size` property for more information.
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* textcolor - color. Default white.
+* model
+ * bgcolor - color, sets background color.
+ * noclip - boolean, set to true to allow the element to exceed formspec bounds.
+ * Default to false in formspec_version version 3 or higher
* image
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
* Default to false in formspec_version version 3 or higher
* item_image
* noclip - boolean, set to true to allow the element to exceed formspec bounds. Default to false.
* label, vertlabel
+ * font - Sets font type. See button `font` property for more information.
+ * font_size - Sets font size. See button `font_size` property for more information.
+ * noclip - boolean, set to true to allow the element to exceed formspec bounds.
+* list
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
+ * size - 2d vector, sets the size of inventory slots in coordinates.
+ * spacing - 2d vector, sets the space between inventory slots in coordinates.
* image_button (additional properties)
* fgimg - standard image. Defaults to none.
* fgimg_hovered - image when hovered. Defaults to fgimg when not provided.
@@ -2749,9 +2913,17 @@ Some types may inherit styles from parent types.
* fgimg_pressed - image when pressed. Defaults to fgimg when not provided.
* This is deprecated, use states instead.
* NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed
+ * sound - a sound to be played when triggered.
+* scrollbar
+ * noclip - boolean, set to true to allow the element to exceed formspec bounds.
* tabheader
* noclip - boolean, set to true to allow the element to exceed formspec bounds.
+ * sound - a sound to be played when a different tab is selected.
* textcolor - color. Default white.
+* table, textlist
+ * font - Sets font type. See button `font` property for more information.
+ * font_size - Sets font size. See button `font_size` property for more information.
+ * noclip - boolean, set to true to allow the element to exceed formspec bounds.
### Valid States
@@ -2964,7 +3136,8 @@ Internally, it is implemented as a table with the 3 fields
`x`, `y` and `z`. Example: `{x = 0, y = 1, z = 0}`.
For the following functions, `v`, `v1`, `v2` are vectors,
-`p1`, `p2` are positions:
+`p1`, `p2` are positions,
+`s` is a scalar (a number):
* `vector.new(a[, b, c])`:
* Returns a vector.
@@ -2993,10 +3166,12 @@ For the following functions, `v`, `v1`, `v2` are vectors,
* Returns in order minp, maxp vectors of the cuboid defined by `v1`, `v2`.
* `vector.angle(v1, v2)`:
* Returns the angle between `v1` and `v2` in radians.
-* `vector.dot(v1, v2)`
- * Returns the dot product of `v1` and `v2`
-* `vector.cross(v1, v2)`
- * Returns the cross product of `v1` and `v2`
+* `vector.dot(v1, v2)`:
+ * Returns the dot product of `v1` and `v2`.
+* `vector.cross(v1, v2)`:
+ * Returns the cross product of `v1` and `v2`.
+* `vector.offset(v, x, y, z)`:
+ * Returns the sum of the vectors `v` and `{x = x, y = y, z = z}`.
For the following functions `x` can be either a vector or a number:
@@ -3008,10 +3183,12 @@ For the following functions `x` can be either a vector or a number:
* Returns a vector.
* If `x` is a vector: Returns the difference of `v` subtracted by `x`.
* If `x` is a number: Subtracts `x` from each component of `v`.
-* `vector.multiply(v, x)`:
- * Returns a scaled vector or Schur product.
-* `vector.divide(v, x)`:
- * Returns a scaled vector or Schur quotient.
+* `vector.multiply(v, s)`:
+ * Returns a scaled vector.
+ * Deprecated: If `s` is a vector: Returns the Schur product.
+* `vector.divide(v, s)`:
+ * Returns a scaled vector.
+ * Deprecated: If `s` is a vector: Returns the Schur quotient.
For the following functions `a` is an angle in radians and `r` is a rotation
vector ({x = <pitch>, y = <yaw>, z = <roll>}) where pitch, yaw and roll are
@@ -3091,6 +3268,7 @@ Helper functions
* returns true when the passed number represents NaN.
* `minetest.get_us_time()`
* returns time with microsecond precision. May not return wall time.
+ * This value might overflow on certain 32-bit systems!
* `table.copy(table)`: returns a table
* returns a deep copy of `table`
* `table.indexof(list, val)`: returns the smallest numerical index containing
@@ -3101,7 +3279,8 @@ Helper functions
* Appends all values in `other_table` to `table` - uses `#table + 1` to
find new indices.
* `table.key_value_swap(t)`: returns a table with keys and values swapped
- * If multiple keys in `t` map to the same value, the result is undefined.
+ * If multiple keys in `t` map to the same value, it is unspecified which
+ value maps to that key.
* `table.shuffle(table, [from], [to], [random_func])`:
* Shuffles elements `from` to `to` in `table` in place
* `from` defaults to `1`
@@ -4039,6 +4218,8 @@ Callbacks:
* Called when the object is instantiated.
* `dtime_s` is the time passed since the object was unloaded, which can be
used for updating the entity state.
+* `on_deactivate(self)
+ * Called when the object is about to get removed or unloaded.
* `on_step(self, dtime)`
* Called on every server tick, after movement and collision processing.
`dtime` is usually 0.1 seconds, as per the `dedicated_server_step` setting
@@ -4159,11 +4340,14 @@ Utilities
* `minetest.get_current_modname()`: returns the currently loading mod's name,
when loading a mod.
-* `minetest.get_modpath(modname)`: returns e.g.
- `"/home/user/.minetest/usermods/modname"`.
- * Useful for loading additional `.lua` modules or static data from mod
-* `minetest.get_modnames()`: returns a list of installed mods
- * Return a list of installed mods, sorted alphabetically
+* `minetest.get_modpath(modname)`: returns the directory path for a mod,
+ e.g. `"/home/user/.minetest/usermods/modname"`.
+ * Returns nil if the mod is not enabled or does not exist (not installed).
+ * Works regardless of whether the mod has been loaded yet.
+ * Useful for loading additional `.lua` modules or static data from a mod,
+ or checking if a mod is enabled.
+* `minetest.get_modnames()`: returns a list of enabled mods, sorted alphabetically.
+ * Does not include disabled mods, even if they are installed.
* `minetest.get_worldpath()`: returns e.g. `"/home/user/.minetest/world"`
* Useful for storing custom data
* `minetest.is_singleplayer()`
@@ -4204,6 +4388,10 @@ Utilities
pathfinder_works = true,
-- Whether Collision info is available to an objects' on_step (5.3.0)
object_step_has_moveresult = true,
+ -- Whether get_velocity() and add_velocity() can be used on players (5.4.0)
+ direct_velocity_on_players = true,
+ -- nodedef's use_texture_alpha accepts new string modes (5.4.0)
+ use_texture_alpha_string_modes = true,
}
* `minetest.has_feature(arg)`: returns `boolean, missing_features`
@@ -4412,6 +4600,10 @@ Call these functions only at load time!
the puncher to the punched.
* `damage`: Number that represents the damage calculated by the engine
* should return `true` to prevent the default damage mechanism
+* `minetest.register_on_rightclickplayer(function(player, clicker))`
+ * Called when a player is right-clicked
+ * `player`: ObjectRef - Player that was right-clicked
+ * `clicker`: ObjectRef - Object that right-clicked, may or may not be a player
* `minetest.register_on_player_hpchange(function(player, hp_change, reason), modifier)`
* Called when the player gets damaged or healed
* `player`: ObjectRef of the player
@@ -4470,6 +4662,11 @@ Call these functions only at load time!
* Called always when a player says something
* Return `true` to mark the message as handled, which means that it will
not be sent to other players.
+* `minetest.register_on_chatcommand(function(name, command, params))`
+ * Called always when a chatcommand is triggered, before `minetest.registered_chatcommands`
+ is checked to see if the command exists, but after the input is parsed.
+ * Return `true` to mark the command as handled, which means that the default
+ handlers will be prevented.
* `minetest.register_on_player_receive_fields(function(player, formname, fields))`
* Called when the server received input from `player` in a formspec with
the given `formname`. Specifically, this is called on any of the
@@ -4477,7 +4674,7 @@ Call these functions only at load time!
* a button was pressed,
* Enter was pressed while the focus was on a text field
* a checkbox was toggled,
- * something was selecteed in a drop-down list,
+ * something was selected in a dropdown list,
* a different tab was selected,
* selection was changed in a textlist or table,
* an entry was double-clicked in a textlist or table,
@@ -4491,7 +4688,8 @@ Call these functions only at load time!
* `button` and variants: If pressed, contains the user-facing button
text as value. If not pressed, is `nil`
* `field`, `textarea` and variants: Text in the field
- * `dropdown`: Text of selected item
+ * `dropdown`: Either the index or value, depending on the `index event`
+ dropdown argument.
* `tabheader`: Tab index, starting with `"1"` (only if tab changed)
* `checkbox`: `"true"` if checked, `"false"` if unchecked
* `textlist`: See `minetest.explode_textlist_event`
@@ -4671,6 +4869,22 @@ Environment access
* `pos`: The position where to measure the light.
* `timeofday`: `nil` for current time, `0` for night, `0.5` for day
* Returns a number between `0` and `15` or `nil`
+ * `nil` is returned e.g. when the map isn't loaded at `pos`
+* `minetest.get_natural_light(pos[, timeofday])`
+ * Figures out the sunlight (or moonlight) value at pos at the given time of
+ day.
+ * `pos`: The position of the node
+ * `timeofday`: `nil` for current time, `0` for night, `0.5` for day
+ * Returns a number between `0` and `15` or `nil`
+ * This function tests 203 nodes in the worst case, which happens very
+ unlikely
+* `minetest.get_artificial_light(param1)`
+ * Calculates the artificial light (light from e.g. torches) value from the
+ `param1` value.
+ * `param1`: The param1 value of a `paramtype = "light"` node.
+ * Returns a number between `0` and `15`
+ * Currently it's the same as `math.floor(param1 / 16)`, except that it
+ ensures compatibility.
* `minetest.place_node(pos, node)`
* Place node with the same effects that a player would cause
* `minetest.dig_node(pos)`
@@ -4699,6 +4913,9 @@ Environment access
* `minetest.get_objects_inside_radius(pos, radius)`: returns a list of
ObjectRefs.
* `radius`: using an euclidean metric
+* `minetest.get_objects_in_area(pos1, pos2)`: returns a list of
+ ObjectRefs.
+ * `pos1` and `pos2` are the min and max positions of the area to search.
* `minetest.set_timeofday(val)`
* `val` is between `0` and `1`; `0` for midnight, `0.5` for midday
* `minetest.get_timeofday()`
@@ -4713,12 +4930,15 @@ Environment access
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* `search_center` is an optional boolean (default: `false`)
If true `pos` is also checked for the nodes
-* `minetest.find_nodes_in_area(pos1, pos2, nodenames)`: returns a list of
- positions.
+* `minetest.find_nodes_in_area(pos1, pos2, nodenames, [grouped])`
+ * `pos1` and `pos2` are the min and max positions of the area to search.
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
- * First return value: Table with all node positions
- * Second return value: Table with the count of each node with the node name
- as index.
+ * If `grouped` is true the return value is a table indexed by node name
+ which contains lists of positions.
+ * If `grouped` is false or absent the return values are as follows:
+ first value: Table with all node positions
+ second value: Table with the count of each node with the node name
+ as index
* Area volume is limited to 4,096,000 nodes
* `minetest.find_nodes_in_area_under_air(pos1, pos2, nodenames)`: returns a
list of positions.
@@ -5186,17 +5406,23 @@ Sounds
* `minetest.sound_fade(handle, step, gain)`
* `handle` is a handle returned by `minetest.sound_play`
* `step` determines how fast a sound will fade.
- Negative step will lower the sound volume, positive step will increase
- the sound volume.
+ The gain will change by this much per second,
+ until it reaches the target gain.
+ Note: Older versions used a signed step. This is deprecated, but old
+ code will still work. (the client uses abs(step) to correct it)
* `gain` the target gain for the fade.
+ Fading to zero will delete the sound.
Timing
------
-* `minetest.after(time, func, ...)`
+* `minetest.after(time, func, ...)` : returns job table to use as below.
* Call the function `func` after `time` seconds, may be fractional
* Optional: Variable number of arguments that are passed to `func`
+* `job:cancel()`
+ * Cancels the job function from being called
+
Server
------
@@ -5223,20 +5449,22 @@ Server
* Returns a code (0: successful, 1: no such player, 2: player is connected)
* `minetest.remove_player_auth(name)`: remove player authentication data
* Returns boolean indicating success (false if player nonexistant)
-* `minetest.dynamic_add_media(filepath)`
- * Adds the file at the given path to the media sent to clients by the server
- on startup and also pushes this file to already connected clients.
+* `minetest.dynamic_add_media(filepath, callback)`
+ * `filepath`: path to a media file on the filesystem
+ * `callback`: function with arguments `name`, where name is a player name
+ (previously there was no callback argument; omitting it is deprecated)
+ * Adds the file to the media sent to clients by the server on startup
+ and also pushes this file to already connected clients.
The file must be a supported image, sound or model format. It must not be
modified, deleted, moved or renamed after calling this function.
The list of dynamically added media is not persisted.
- * Returns boolean indicating success (duplicate files count as error)
- * The media will be ready to use (in e.g. entity textures, sound_play)
- immediately after calling this function.
+ * Returns false on error, true if the request was accepted
+ * The given callback will be called for every player as soon as the
+ media is available on the client.
Old clients that lack support for this feature will not see the media
- unless they reconnect to the server.
- * Since media transferred this way does not use client caching or HTTP
- transfers, dynamic media should not be used with big files or performance
- will suffer.
+ unless they reconnect to the server. (callback won't be called)
+ * Since media transferred this way currently does not use client caching
+ or HTTP transfers, dynamic media should not be used with big files.
Bans
----
@@ -5616,6 +5844,9 @@ Global tables
* Map of registered tool definitions, indexed by name
* `minetest.registered_entities`
* Map of registered entity prototypes, indexed by name
+ * Values in this table may be modified directly.
+ Note: changes to initial properties will only affect entities spawned afterwards,
+ as they are only read when spawning.
* `minetest.object_refs`
* Map of object references, indexed by active object id
* `minetest.luaentities`
@@ -5646,6 +5877,7 @@ Global tables
* Map of registered chat command definitions, indexed by name
* `minetest.registered_privileges`
* Map of registered privilege definitions, indexed by name
+ * Registered privileges can be modified directly in this table.
### Registered callback tables
@@ -5760,6 +5992,31 @@ An `InvRef` is a reference to an inventory.
`minetest.get_inventory(location)`.
* returns `{type="undefined"}` in case location is not known
+### Callbacks
+
+Detached & nodemeta inventories provide the following callbacks for move actions:
+
+#### Before
+
+The `allow_*` callbacks return how many items can be moved.
+
+* `allow_move`/`allow_metadata_inventory_move`: Moving items in the inventory
+* `allow_take`/`allow_metadata_inventory_take`: Taking items from the inventory
+* `allow_put`/`allow_metadata_inventory_put`: Putting items to the inventory
+
+#### After
+
+The `on_*` callbacks are called after the items have been placed in the inventories.
+
+* `on_move`/`on_metadata_inventory_move`: Moving items in the inventory
+* `on_take`/`on_metadata_inventory_take`: Taking items from the inventory
+* `on_put`/`on_metadata_inventory_put`: Putting items to the inventory
+
+#### Swapping
+
+When a player tries to put an item to a place where another item is, the items are *swapped*.
+This means that all callbacks will be called twice (once for each action).
+
`ItemStack`
-----------
@@ -5785,6 +6042,18 @@ an itemstring, a table or `nil`.
stack).
* `set_metadata(metadata)`: (DEPRECATED) Returns true.
* `get_description()`: returns the description shown in inventory list tooltips.
+ * The engine uses this when showing item descriptions in tooltips.
+ * Fields for finding the description, in order:
+ * `description` in item metadata (See [Item Metadata].)
+ * `description` in item definition
+ * item name
+* `get_short_description()`: returns the short description or nil.
+ * Unlike the description, this does not include new lines.
+ * Fields for finding the short description, in order:
+ * `short_description` in item metadata (See [Item Metadata].)
+ * `short_description` in item definition
+ * first line of the description (From item meta or def, see `get_description()`.)
+ * Returns nil if none of the above are set
* `clear()`: removes all items from the stack, making it empty.
* `replace(item)`: replace the contents of this stack.
* `item` can also be an itemstring or table.
@@ -5938,6 +6207,19 @@ object you are working with still exists.
* `get_pos()`: returns `{x=num, y=num, z=num}`
* `set_pos(pos)`: `pos`=`{x=num, y=num, z=num}`
+* `get_velocity()`: returns the velocity, a vector.
+* `add_velocity(vel)`
+ * `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}`
+ * In comparison to using get_velocity, adding the velocity and then using
+ set_velocity, add_velocity is supposed to avoid synchronization problems.
+ Additionally, players also do not support set_velocity.
+ * If a player:
+ * Does not apply during free_move.
+ * Note that since the player speed is normalized at each move step,
+ increasing e.g. Y velocity beyond what would usually be achieved
+ (see: physics overrides) will cause existing X/Z velocity to be reduced.
+ * Example: `add_velocity({x=0, y=6.5, z=0})` is equivalent to
+ pressing the jump key (assuming default settings)
* `move_to(pos, continuous=false)`
* Does an interpolated move for Lua entities for visually smooth transitions.
* If `continuous` is true, the Lua entity will not be moved to the current
@@ -5948,8 +6230,8 @@ object you are working with still exists.
* `time_from_last_punch` = time since last punch action of the puncher
* `direction`: can be `nil`
* `right_click(clicker)`; `clicker` is another `ObjectRef`
-* `get_hp()`: returns number of hitpoints (2 * number of hearts)
-* `set_hp(hp, reason)`: set number of hitpoints (2 * number of hearts).
+* `get_hp()`: returns number of health points
+* `set_hp(hp, reason)`: set number of health points
* See reason in register_on_player_hpchange
* Is limited to the range of 0 ... 65535 (2^16 - 1)
* For players: HP are also limited by `hp_max` specified in the player's
@@ -5972,17 +6254,22 @@ object you are working with still exists.
`frame_loop`.
* `set_animation_frame_speed(frame_speed)`
* `frame_speed`: number, default: `15.0`
-* `set_attach(parent, bone, position, rotation)`
- * `bone`: string
- * `position`: `{x=num, y=num, z=num}` (relative)
- * `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees
-* `get_attach()`: returns parent, bone, position, rotation or nil if it isn't
- attached.
+* `set_attach(parent[, bone, position, rotation, forced_visible])`
+ * `bone`: string. Default is `""`, the root bone
+ * `position`: `{x=num, y=num, z=num}`, relative, default `{x=0, y=0, z=0}`
+ * `rotation`: `{x=num, y=num, z=num}` = Rotation on each axis, in degrees.
+ Default `{x=0, y=0, z=0}`
+ * `forced_visible`: Boolean to control whether the attached entity
+ should appear in first person. Default `false`.
+* `get_attach()`: returns parent, bone, position, rotation, forced_visible,
+ or nil if it isn't attached.
+* `get_children()`: returns a list of ObjectRefs that are attached to the
+ object.
* `set_detach()`
-* `set_bone_position(bone, position, rotation)`
- * `bone`: string
- * `position`: `{x=num, y=num, z=num}` (relative)
- * `rotation`: `{x=num, y=num, z=num}`
+* `set_bone_position([bone, position, rotation])`
+ * `bone`: string. Default is `""`, the root bone
+ * `position`: `{x=num, y=num, z=num}`, relative, `default {x=0, y=0, z=0}`
+ * `rotation`: `{x=num, y=num, z=num}`, default `{x=0, y=0, z=0}`
* `get_bone_position(bone)`: returns position and rotation of the bone
* `set_properties(object property table)`
* `get_properties()`: returns object property table
@@ -5990,15 +6277,21 @@ object you are working with still exists.
* `get_nametag_attributes()`
* returns a table with the attributes of the nametag of an object
* {
- color = {a=0..255, r=0..255, g=0..255, b=0..255},
text = "",
+ color = {a=0..255, r=0..255, g=0..255, b=0..255},
+ bgcolor = {a=0..255, r=0..255, g=0..255, b=0..255},
}
* `set_nametag_attributes(attributes)`
* sets the attributes of the nametag of an object
* `attributes`:
{
- color = ColorSpec,
text = "My Nametag",
+ color = ColorSpec,
+ -- ^ Text color
+ bgcolor = ColorSpec or false,
+ -- ^ Sets background color of nametag
+ -- `false` will cause the background to be set automatically based on user settings
+ -- Default: false
}
#### Lua entity only (no-op for other objects)
@@ -6009,11 +6302,6 @@ object you are working with still exists.
no effect and returning `nil`.
* `set_velocity(vel)`
* `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}`
-* `add_velocity(vel)`
- * `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}`
- * In comparison to using get_velocity, adding the velocity and then using
- set_velocity, add_velocity is supposed to avoid synchronization problems.
-* `get_velocity()`: returns the velocity, a vector
* `set_acceleration(acc)`
* `acc` is a vector
* `get_acceleration()`: returns the acceleration, a vector
@@ -6021,35 +6309,37 @@ object you are working with still exists.
* `rot` is a vector (radians). X is pitch (elevation), Y is yaw (heading)
and Z is roll (bank).
* `get_rotation()`: returns the rotation, a vector (radians)
-* `set_yaw(radians)`: sets the yaw (heading).
+* `set_yaw(yaw)`: sets the yaw in radians (heading).
* `get_yaw()`: returns number in radians
* `set_texture_mod(mod)`
+ * Set a texture modifier to the base texture, for sprites and meshes.
+ * When calling `set_texture_mod` again, the previous one is discarded.
+ * `mod` the texture modifier. See [Texture modifiers].
* `get_texture_mod()` returns current texture modifier
-* `set_sprite(p, num_frames, framelength, select_horiz_by_yawpitch)`
- * Select sprite from spritesheet with optional animation and Dungeon Master
- style texture selection based on yaw relative to camera
- * `p`: {x=number, y=number}, the coordinate of the first frame
- (x: column, y: row), default: `{x=0, y=0}`
- * `num_frames`: number, default: `1`
- * `framelength`: number, default: `0.2`
- * `select_horiz_by_yawpitch`: boolean, this was once used for the Dungeon
- Master mob, default: `false`
+* `set_sprite(start_frame, num_frames, framelength, select_x_by_camera)`
+ * Specifies and starts a sprite animation
+ * Animations iterate along the frame `y` position.
+ * `start_frame`: {x=column number, y=row number}, the coordinate of the
+ first frame, default: `{x=0, y=0}`
+ * `num_frames`: Total frames in the texture, default: `1`
+ * `framelength`: Time per animated frame in seconds, default: `0.2`
+ * `select_x_by_camera`: Only for visual = `sprite`. Changes the frame `x`
+ position according to the view direction. default: `false`.
+ * First column: subject facing the camera
+ * Second column: subject looking to the left
+ * Third column: subject backing the camera
+ * Fourth column: subject looking to the right
+ * Fifth column: subject viewed from above
+ * Sixth column: subject viewed from below
* `get_entity_name()` (**Deprecated**: Will be removed in a future version)
* `get_luaentity()`
#### Player only (no-op for other objects)
* `get_player_name()`: returns `""` if is not a player
-* `get_player_velocity()`: returns `nil` if is not a player, otherwise a
+* `get_player_velocity()`: **DEPRECATED**, use get_velocity() instead.
table {x, y, z} representing the player's instantaneous velocity in nodes/s
-* `add_player_velocity(vel)`
- * Adds to player velocity, this happens client-side and only once.
- * Does not apply during free_move.
- * Note that since the player speed is normalized at each move step,
- increasing e.g. Y velocity beyond what would usually be achieved
- (see: physics overrides) will cause existing X/Z velocity to be reduced.
- * Example: `add_player_velocity({x=0, y=6.5, z=0})` is equivalent to
- pressing the jump key (assuming default settings)
+* `add_player_velocity(vel)`: **DEPRECATED**, use add_velocity(vel) instead.
* `get_look_dir()`: get camera direction as a unit vector
* `get_look_vertical()`: pitch in radians
* Angle ranges between -pi/2 and pi/2, which are straight up and down
@@ -6111,15 +6401,23 @@ object you are working with still exists.
* Only affects formspecs shown after this is called.
* `get_formspec_prepend(formspec)`: returns a formspec string.
* `get_player_control()`: returns table with player pressed keys
- * The table consists of fields with boolean value representing the pressed
- keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up, zoom.
- * example: `{jump=false, right=true, left=false, LMB=false, RMB=false,
- sneak=true, aux1=false, down=false, up=false, zoom=false}`
- * The `zoom` field is available since 5.3
+ * The table consists of fields with the following boolean values
+ representing the pressed keys: `up`, `down`, `left`, `right`, `jump`,
+ `aux1`, `sneak`, `dig`, `place`, `LMB`, `RMB`, and `zoom`.
+ * The fields `LMB` and `RMB` are equal to `dig` and `place` respectively,
+ and exist only to preserve backwards compatibility.
* `get_player_control_bits()`: returns integer with bit packed player pressed
- keys.
- * bit nr/meaning: 0/up, 1/down, 2/left, 3/right, 4/jump, 5/aux1, 6/sneak,
- 7/LMB, 8/RMB, 9/zoom (zoom available since 5.3)
+ keys. Bits:
+ * 0 - up
+ * 1 - down
+ * 2 - left
+ * 3 - right
+ * 4 - jump
+ * 5 - aux1
+ * 6 - sneak
+ * 7 - dig
+ * 8 - place
+ * 9 - zoom
* `set_physics_override(override_table)`
* `override_table` is a table with the following fields:
* `speed`: multiplier to default walking speed value (default: `1`)
@@ -6164,8 +6462,31 @@ object you are working with still exists.
* `hud_set_hotbar_selected_image(texturename)`
* sets image for selected item of hotbar
* `hud_get_hotbar_selected_image`: returns texturename
-* `set_sky(parameters)`
- * `parameters` is a table with the following optional fields:
+* `set_minimap_modes({mode, mode, ...}, selected_mode)`
+ * Overrides the available minimap modes (and toggle order), and changes the
+ selected mode.
+ * `mode` is a table consisting of up to four fields:
+ * `type`: Available type:
+ * `off`: Minimap off
+ * `surface`: Minimap in surface mode
+ * `radar`: Minimap in radar mode
+ * `texture`: Texture to be displayed instead of terrain map
+ (texture is centered around 0,0 and can be scaled).
+ Texture size is limited to 512 x 512 pixel.
+ * `label`: Optional label to display on minimap mode toggle
+ The translation must be handled within the mod.
+ * `size`: Sidelength or diameter, in number of nodes, of the terrain
+ displayed in minimap
+ * `texture`: Only for texture type, name of the texture to display
+ * `scale`: Only for texture type, scale of the texture map in nodes per
+ pixel (for example a `scale` of 2 means each pixel represents a 2x2
+ nodes square)
+ * `selected_mode` is the mode index to be selected after modes have been changed
+ (0 is the first mode).
+* `set_sky(sky_parameters)`
+ * The presence of the function `set_sun`, `set_moon` or `set_stars` indicates
+ whether `set_sky` accepts this format. Check the legacy format otherwise.
+ * `sky_parameters` is a table with the following optional fields:
* `base_color`: ColorSpec, changes fog in "skybox" and "plain".
* `type`: Available types:
* `"regular"`: Uses 0 textures, `base_color` ignored
@@ -6206,11 +6527,20 @@ object you are working with still exists.
abides by, `"custom"` uses `sun_tint` and `moon_tint`, while
`"default"` uses the classic Minetest sun and moon tinting.
Will use tonemaps, if set to `"default"`. (default: `"default"`)
+* `set_sky(base_color, type, {texture names}, clouds)`
+ * Deprecated. Use `set_sky(sky_parameters)`
+ * `base_color`: ColorSpec, defaults to white
+ * `type`: Available types:
+ * `"regular"`: Uses 0 textures, `bgcolor` ignored
+ * `"skybox"`: Uses 6 textures, `bgcolor` used
+ * `"plain"`: Uses 0 textures, `bgcolor` used
+ * `clouds`: Boolean for whether clouds appear in front of `"skybox"` or
+ `"plain"` custom skyboxes (default: `true`)
* `get_sky()`: returns base_color, type, table of textures, clouds.
* `get_sky_color()`: returns a table with the `sky_color` parameters as in
`set_sky`.
-* `set_sun(parameters)`:
- * `parameters` is a table with the following optional fields:
+* `set_sun(sun_parameters)`:
+ * `sun_parameters` is a table with the following optional fields:
* `visible`: Boolean for whether the sun is visible.
(default: `true`)
* `texture`: A regular texture for the sun. Setting to `""`
@@ -6224,8 +6554,8 @@ object you are working with still exists.
* `scale`: Float controlling the overall size of the sun. (default: `1`)
* `get_sun()`: returns a table with the current sun parameters as in
`set_sun`.
-* `set_moon(parameters)`:
- * `parameters` is a table with the following optional fields:
+* `set_moon(moon_parameters)`:
+ * `moon_parameters` is a table with the following optional fields:
* `visible`: Boolean for whether the moon is visible.
(default: `true`)
* `texture`: A regular texture for the moon. Setting to `""`
@@ -6235,8 +6565,8 @@ object you are working with still exists.
* `scale`: Float controlling the overall size of the moon (default: `1`)
* `get_moon()`: returns a table with the current moon parameters as in
`set_moon`.
-* `set_stars(parameters)`:
- * `parameters` is a table with the following optional fields:
+* `set_stars(star_parameters)`:
+ * `star_parameters` is a table with the following optional fields:
* `visible`: Boolean for whether the stars are visible.
(default: `true`)
* `count`: Integer number to set the number of stars in
@@ -6248,8 +6578,8 @@ object you are working with still exists.
* `scale`: Float controlling the overall size of the stars (default: `1`)
* `get_stars()`: returns a table with the current stars parameters as in
`set_stars`.
-* `set_clouds(parameters)`: set cloud parameters
- * `parameters` is a table with the following optional fields:
+* `set_clouds(cloud_parameters)`: set cloud parameters
+ * `cloud_parameters` is a table with the following optional fields:
* `density`: from `0` (no clouds) to `1` (full clouds) (default `0.4`)
* `color`: basic cloud color with alpha channel, ColorSpec
(default `#fff0f0e5`).
@@ -6267,21 +6597,17 @@ object you are working with still exists.
amount.
* `nil`: Disables override, defaulting to sunlight based on day-night cycle
* `get_day_night_ratio()`: returns the ratio or nil if it isn't overridden
-* `set_local_animation(stand/idle, walk, dig, walk+dig, frame_speed=frame_speed)`:
- set animation for player model in third person view
-
- set_local_animation({x=0, y=79}, -- stand/idle animation key frames
- {x=168, y=187}, -- walk animation key frames
- {x=189, y=198}, -- dig animation key frames
- {x=200, y=219}, -- walk+dig animation key frames
- frame_speed=30) -- animation frame speed
-* `get_local_animation()`: returns stand, walk, dig, dig+walk tables and
+* `set_local_animation(idle, walk, dig, walk_while_dig, frame_speed)`:
+ set animation for player model in third person view.
+ * Every animation equals to a `{x=starting frame, y=ending frame}` table.
+ * `frame_speed` sets the animations frame speed. Default is 30.
+* `get_local_animation()`: returns idle, walk, dig, walk_while_dig tables and
`frame_speed`.
-* `set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})`: defines offset value for
- camera per player.
+* `set_eye_offset([firstperson, thirdperson])`: defines offset vectors for
+ camera per player. An argument defaults to `{x=0, y=0, z=0}` if unspecified.
* in first person view
* in third person view (max. values `{x=-10/10,y=-10,15,z=-5/5}`)
-* `get_eye_offset()`: returns `offset_first` and `offset_third`
+* `get_eye_offset()`: returns first and third person offsets.
* `send_mapblock(blockpos)`:
* Sends a server-side loaded mapblock to the player.
* Returns `false` if failed.
@@ -6634,10 +6960,18 @@ Player properties need to be saved manually.
-- in mods.
nametag = "",
- -- By default empty, for players their name is shown if empty
+ -- The name to display on the head of the object. By default empty.
+ -- If the object is a player, a nil or empty nametag is replaced by the player's name.
+ -- For all other objects, a nil or empty string removes the nametag.
+ -- To hide a nametag, set its color alpha to zero. That will disable it entirely.
nametag_color = <ColorSpec>,
- -- Sets color of nametag
+ -- Sets text color of nametag
+
+ nametag_bgcolor = <ColorSpec>,
+ -- Sets background color of nametag
+ -- `false` will cause the background to be set automatically based on user settings.
+ -- Default: false
infotext = "",
-- By default empty, text to be shown when pointed at object
@@ -6653,6 +6987,10 @@ Player properties need to be saved manually.
shaded = true,
-- Setting this to 'false' disables diffuse lighting of entity
+
+ show_on_minimap = false,
+ -- Defaults to true for players, false for other entities.
+ -- If set to true the entity will show as a marker on the minimap.
}
Entity definition
@@ -6787,13 +7125,8 @@ Tile definition
* `"image.png"`
* `{name="image.png", animation={Tile Animation definition}}`
-* `{name="image.png", backface_culling=bool, tileable_vertical=bool,
- tileable_horizontal=bool, align_style="node"/"world"/"user", scale=int}`
+* `{name="image.png", backface_culling=bool, align_style="node"/"world"/"user", scale=int}`
* backface culling enabled by default for most nodes
- * tileable flags are info for shaders, how they should treat texture
- when displacement mapping is used.
- Directions are from the point of view of the tile texture,
- not the node it's on.
* align style determines whether the texture will be rotated with the node
or kept aligned with its surroundings. "user" means that client
setting will be used, similar to `glasslike_framed_optional`.
@@ -6846,6 +7179,14 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and
{
description = "Steel Axe",
+ -- Can contain new lines. "\n" has to be used as new line character.
+ -- See also: `get_description` in [`ItemStack`]
+
+ short_description = "Steel Axe",
+ -- Must not contain new lines.
+ -- Defaults to nil.
+ -- Use an [`ItemStack`] to get the short description, eg:
+ -- ItemStack(itemname):get_short_description()
groups = {},
-- key = name, value = rating; rating = 1..3.
@@ -6884,6 +7225,13 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and
liquids_pointable = false,
+ light_source = 0,
+ -- When used for nodes: Defines amount of light emitted by node.
+ -- Otherwise: Defines texture glow when viewed as a dropped item
+ -- To set the maximum (14), use the value 'minetest.LIGHT_MAX'.
+ -- A value outside the range 0 to minetest.LIGHT_MAX causes undefined
+ -- behavior.
+
-- See "Tools" section for an example including explanation
tool_capabilities = {
full_punch_interval = 1.0,
@@ -7011,8 +7359,18 @@ Used by `minetest.register_node`.
-- If the node has a palette, then this setting only has an effect in
-- the inventory and on the wield item.
- use_texture_alpha = false,
- -- Use texture's alpha channel
+ use_texture_alpha = ...,
+ -- Specifies how the texture's alpha channel will be used for rendering.
+ -- possible values:
+ -- * "opaque": Node is rendered opaque regardless of alpha channel
+ -- * "clip": A given pixel is either fully see-through or opaque
+ -- depending on the alpha channel being below/above 50% in value
+ -- * "blend": The alpha channel specifies how transparent a given pixel
+ -- of the rendered node is
+ -- The default is "opaque" for drawtypes normal, liquid and flowingliquid;
+ -- "clip" otherwise.
+ -- If set to a boolean value (deprecated): true either sets it to blend
+ -- or clip, false sets it to clip or opaque mode depending on the drawtype.
palette = "palette.png",
-- The node's `param2` is used to select a pixel from the image.
@@ -7075,18 +7433,13 @@ Used by `minetest.register_node`.
leveled_max = 127,
-- Maximum value for `leveled` (0-127), enforced in
-- `minetest.set_node_level` and `minetest.add_node_level`.
+ -- Values above 124 might causes collision detection issues.
liquid_range = 8, -- Number of flowing nodes around source (max. 8)
drowning = 0,
-- Player will take this amount of damage if no bubbles are left
- light_source = 0,
- -- Amount of light emitted by node.
- -- To set the maximum (14), use the value 'minetest.LIGHT_MAX'.
- -- A value outside the range 0 to minetest.LIGHT_MAX causes undefined
- -- behavior.
-
damage_per_second = 0,
-- If player is inside node, this damage is caused
@@ -7108,6 +7461,7 @@ Used by `minetest.register_node`.
type = "fixed",
fixed = {
{-2 / 16, -0.5, -2 / 16, 2 / 16, 3 / 16, 2 / 16},
+ -- Node box format: see [Node boxes]
},
},
-- Custom selection box definition. Multiple boxes can be defined.
@@ -7118,13 +7472,12 @@ Used by `minetest.register_node`.
type = "fixed",
fixed = {
{-2 / 16, -0.5, -2 / 16, 2 / 16, 3 / 16, 2 / 16},
+ -- Node box format: see [Node boxes]
},
},
-- Custom collision box definition. Multiple boxes can be defined.
-- If "nodebox" drawtype is used and collision_box is nil, then node_box
-- definition is used for the collision box.
- -- Both of the boxes above are defined as:
- -- {xmin, ymin, zmin, xmax, ymax, zmax} in nodes from node center.
-- Support maps made in and before January 2012
legacy_facedir_simple = false,
@@ -7165,10 +7518,13 @@ Used by `minetest.register_node`.
-- Node was placed. Also played after falling
place_failed = <SimpleSoundSpec>,
- -- When node placement failed
+ -- When node placement failed.
+ -- Note: This happens if the _built-in_ node placement failed.
+ -- This sound will still be played if the node is placed in the
+ -- `on_place` callback manually.
fall = <SimpleSoundSpec>,
- -- When node starts to fall
+ -- When node starts to fall or is detached
},
drop = "",
@@ -7289,6 +7645,8 @@ Used by `minetest.register_node`.
on_dig = function(pos, node, digger),
-- default: minetest.node_dig
-- By default checks privileges, wears out tool and removes node.
+ -- return true if the node was dug successfully, false otherwise.
+ -- Deprecated: returning nil is the same as returning true.
on_timer = function(pos, elapsed),
-- default: nil
@@ -7328,6 +7686,13 @@ Used by `minetest.register_node`.
-- intensity: 1.0 = mid range of regular TNT.
-- If defined, called when an explosion touches the node, instead of
-- removing the node.
+
+ mod_origin = "modname",
+ -- stores which mod actually registered a node
+ -- if it can not find a source, returns "??"
+ -- useful for getting what mod truly registered something
+ -- example: if a node is registered as ":othermodname:nodename",
+ -- nodename will show "othermodname", but mod_orgin will say "modname"
}
Crafting recipes
@@ -7755,6 +8120,8 @@ Used by `minetest.register_chatcommand`.
func = function(name, param),
-- Called when command is run. Returns boolean success and text output.
+ -- Special case: The help message is shown to the player if `func`
+ -- returns false without a text output.
}
Note that in params, use of symbols is as follows:
@@ -7837,7 +8204,8 @@ Used by `Player:hud_add`. Returned by `Player:hud_get`.
{
hud_elem_type = "image", -- See HUD element types
- -- Type of element, can be "image", "text", "statbar", or "inventory"
+ -- Type of element, can be "image", "text", "statbar", "inventory",
+ -- "compass" or "minimap"
position = {x=0.5, y=0.5},
-- Left corner position of element
@@ -8016,11 +8384,13 @@ Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`.
timeout = 10,
-- Timeout for connection in seconds. Default is 3 seconds.
- post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"},
- -- Optional, if specified a POST request with post_data is performed.
+ method = "GET", "POST", "PUT" or "DELETE"
+ -- The http method to use. Defaults to "GET".
+
+ data = "Raw request data string" OR {field1 = "data1", field2 = "data2"},
+ -- Data for the POST, PUT or DELETE request.
-- Accepts both a string and a table. If a table is specified, encodes
-- table as x-www-form-urlencoded key-value pairs.
- -- If post_data is not specified, a GET request is performed instead.
user_agent = "ExampleUserAgent",
-- Optional, if specified replaces the default minetest user agent with
@@ -8034,6 +8404,10 @@ Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`.
multipart = boolean
-- Optional, if true performs a multipart HTTP request.
-- Default is false.
+ -- Post only, data must be array
+
+ post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"},
+ -- Deprecated, use `data` instead. Forces `method = "POST"`.
}
`HTTPRequestResult` definition
diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt
index 724e37855..b3975bc1d 100644
--- a/doc/menu_lua_api.txt
+++ b/doc/menu_lua_api.txt
@@ -1,4 +1,4 @@
-Minetest Lua Mainmenu API Reference 5.3.0
+Minetest Lua Mainmenu API Reference 5.4.0
=========================================
Introduction
@@ -43,10 +43,14 @@ core.get_max_supp_proto()
core.open_url(url)
^ opens the URL in a web browser, returns false on failure.
^ Must begin with http:// or https://
+core.open_dir(path)
+^ opens the path in the system file browser/explorer, returns false on failure.
+^ Must be an existing directory.
core.get_version() (possible in async calls)
^ returns current core version
+
Filesystem
----------
@@ -63,6 +67,8 @@ core.copy_dir(source,destination,keep_soure) (possible in async calls)
^ destination folder
^ keep_source DEFAULT true --> if set to false source is deleted after copying
^ returns true/false
+core.is_dir(path) (possible in async calls)
+^ returns true if path is a valid dir
core.extract_zip(zipfile,destination) [unzip within path required]
^ zipfile to extract
^ destination folder to extract to
@@ -79,6 +85,7 @@ core.get_video_drivers()
core.get_mapgen_names([include_hidden=false]) -> table of map generator algorithms
registered in the core (possible in async calls)
core.get_cache_path() -> path of cache
+core.get_temp_path() -> path of temp folder
HTTP Requests
@@ -207,6 +214,9 @@ Content and Packages
Content - an installed mod, modpack, game, or texture pack (txt)
Package - content which is downloadable from the content db, may or may not be installed.
+* core.get_user_path() (possible in async calls)
+ * returns path to global user data,
+ the directory that contains user-provided mods, worlds, games, and texture packs.
* core.get_modpath() (possible in async calls)
* returns path to global modpath
* core.get_clientmodpath() (possible in async calls)
@@ -244,32 +254,6 @@ Package - content which is downloadable from the content db, may or may not be i
}
-Favorites
----------
-
-core.get_favorites(location) -> list of favorites (possible in async calls)
-^ location: "local" or "online"
-^ returns {
- [1] = {
- clients = <number of clients/nil>,
- clients_max = <maximum number of clients/nil>,
- version = <server version/nil>,
- password = <true/nil>,
- creative = <true/nil>,
- damage = <true/nil>,
- pvp = <true/nil>,
- description = <server description/nil>,
- name = <server name/nil>,
- address = <address of server/nil>,
- port = <port>
- clients_list = <array of clients/nil>
- mods = <array of mods/nil>
- },
- ...
-}
-core.delete_favorite(id, location) -> success
-
-
Logging
-------
diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt
index 94151f1a4..8af2cbad6 100644
--- a/doc/texture_packs.txt
+++ b/doc/texture_packs.txt
@@ -72,7 +72,12 @@ by texture packs. All existing fallback textures can be found in the directory
* `crosshair.png`
* the crosshair texture in the center of the screen. The settings
`crosshair_color` and `crosshair_alpha` are used to create a cross
- when no texture was found
+ when no texture is found.
+
+* `object_crosshair.png`
+ * the crosshair seen when pointing at an object. The settings
+ `crosshair_color` and `crosshair_alpha` are used to create a cross
+ when no texture is found.
* `halo.png`: used for the node highlighting mesh
@@ -85,6 +90,7 @@ by texture packs. All existing fallback textures can be found in the directory
* `minimap_mask_square.png`: mask used for the square minimap
* `minimap_overlay_round.png`: overlay texture for the round minimap
* `minimap_overlay_square.png`: overlay texture for the square minimap
+* `no_texture_airlike.png`: fallback inventory image for airlike nodes
* `object_marker_red.png`: texture for players on the minimap
* `player_marker.png`: texture for the own player on the square minimap
@@ -189,11 +195,27 @@ Here are targets you can choose from:
| bottom | y- face |
| sides | x-, x+, z-, z+ faces |
| all | All faces. You can also use '*' instead of 'all'. |
+| special1 | The first entry in the special_tiles list |
+| special2 | The second entry in the special_tiles list |
+| special3 | The third entry in the special_tiles list |
+| special4 | The fourth entry in the special_tiles list |
+| special5 | The fifth entry in the special_tiles list |
+| special6 | The sixth entry in the special_tiles list |
| inventory | The inventory texture |
| wield | The texture used when held by the player |
Nodes support all targets, but other items only support 'inventory'
-and 'wield'
+and 'wield'.
+
+### Using the special targets
+
+The special* targets only apply to specific drawtypes:
+
+* `flowingliquid`: special1 sets the top texture, special2 sets the side texture
+* `allfaces_optional`: special1 is used by simple mode, see below
+* `glasslike_framed`: When containing a liquid, special1 sets the liquid texture
+* `glasslike_framed_optional`: Same as `glasslike_framed`
+* `plantlike_rooted`: special1 sets the plant's texture
Designing leaves textures for the leaves rendering options
----------------------------------------------------------
diff --git a/doc/world_format.txt b/doc/world_format.txt
index 73a03e5ee..a8a9e463e 100644
--- a/doc/world_format.txt
+++ b/doc/world_format.txt
@@ -493,19 +493,8 @@ Static objects are persistent freely moving objects in the world.
Object types:
1: Test object
-2: Item
-3: Rat (obsolete)
-4: Oerkki (obsolete)
-5: Firefly (obsolete)
-6: MobV2 (obsolete)
7: LuaEntity
-1: Item:
- u8 version
- version 0:
- u16 len
- u8[len] itemstring
-
7: LuaEntity:
u8 compatibility_byte (always 1)
u16 len
diff --git a/fonts/DroidSansFallbackFull-LICENSE.txt b/fonts/DroidSansFallbackFull-LICENSE.txt
index d3a2eaa8c..a7bf409b7 100644
--- a/fonts/DroidSansFallbackFull-LICENSE.txt
+++ b/fonts/DroidSansFallbackFull-LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (C) 2008 The Android Open Source Project
+Copyright (C) 2012 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/fonts/DroidSansFallbackFull.ttf b/fonts/DroidSansFallbackFull.ttf
index a9df00585..0cacabedc 100644
--- a/fonts/DroidSansFallbackFull.ttf
+++ b/fonts/DroidSansFallbackFull.ttf
Binary files differ
diff --git a/games/devtest/README.md b/games/devtest/README.md
index a7e93cf11..77e722af7 100644
--- a/games/devtest/README.md
+++ b/games/devtest/README.md
@@ -23,9 +23,8 @@ Basically, just create a world and start. A few important things to note:
* Use the `/infplace` command to toggle infinite node placement in-game
* Use the Param2 Tool to change the param2 of nodes; it's useful to experiment with the various drawtype test nodes
* Check out the game settings and server commands for additional tests and features
-* Creative Mode does nothing (apart from default engine behavior)
-Confused by a certain node or item? Check out for inline code comments.
+Confused by a certain node or item? Check out for inline code comments. The usages of most tools are explained in their tooltips.
### Example tests
diff --git a/games/devtest/mods/basenodes/init.lua b/games/devtest/mods/basenodes/init.lua
index 8156c4bec..2c808c35e 100644
--- a/games/devtest/mods/basenodes/init.lua
+++ b/games/devtest/mods/basenodes/init.lua
@@ -1,4 +1,4 @@
-local WATER_ALPHA = 160
+local WATER_ALPHA = "^[opacity:" .. 160
local WATER_VISC = 1
local LAVA_VISC = 7
@@ -124,14 +124,16 @@ minetest.register_node("basenodes:pine_needles", {
})
minetest.register_node("basenodes:water_source", {
- description = "Water Source",
+ description = "Water Source".."\n"..
+ "Drowning damage: 1",
drawtype = "liquid",
- tiles = {"default_water.png"},
+ waving = 3,
+ tiles = {"default_water.png"..WATER_ALPHA},
special_tiles = {
- {name = "default_water.png", backface_culling = false},
- {name = "default_water.png", backface_culling = true},
+ {name = "default_water.png"..WATER_ALPHA, backface_culling = false},
+ {name = "default_water.png"..WATER_ALPHA, backface_culling = true},
},
- alpha = WATER_ALPHA,
+ use_texture_alpha = "blend",
paramtype = "light",
walkable = false,
pointable = false,
@@ -148,14 +150,18 @@ minetest.register_node("basenodes:water_source", {
})
minetest.register_node("basenodes:water_flowing", {
- description = "Flowing Water",
+ description = "Flowing Water".."\n"..
+ "Drowning damage: 1",
drawtype = "flowingliquid",
+ waving = 3,
tiles = {"default_water_flowing.png"},
special_tiles = {
- {name = "default_water_flowing.png", backface_culling = false},
- {name = "default_water_flowing.png", backface_culling = false},
+ {name = "default_water_flowing.png"..WATER_ALPHA,
+ backface_culling = false},
+ {name = "default_water_flowing.png"..WATER_ALPHA,
+ backface_culling = false},
},
- alpha = WATER_ALPHA,
+ use_texture_alpha = "blend",
paramtype = "light",
paramtype2 = "flowingliquid",
walkable = false,
@@ -173,14 +179,16 @@ minetest.register_node("basenodes:water_flowing", {
})
minetest.register_node("basenodes:river_water_source", {
- description = "River Water Source",
+ description = "River Water Source".."\n"..
+ "Drowning damage: 1",
drawtype = "liquid",
- tiles = { "default_river_water.png" },
+ waving = 3,
+ tiles = { "default_river_water.png"..WATER_ALPHA },
special_tiles = {
- {name = "default_river_water.png", backface_culling = false},
- {name = "default_river_water.png", backface_culling = true},
+ {name = "default_river_water.png"..WATER_ALPHA, backface_culling = false},
+ {name = "default_river_water.png"..WATER_ALPHA, backface_culling = true},
},
- alpha = WATER_ALPHA,
+ use_texture_alpha = "blend",
paramtype = "light",
walkable = false,
pointable = false,
@@ -199,14 +207,18 @@ minetest.register_node("basenodes:river_water_source", {
})
minetest.register_node("basenodes:river_water_flowing", {
- description = "Flowing River Water",
+ description = "Flowing River Water".."\n"..
+ "Drowning damage: 1",
drawtype = "flowingliquid",
- tiles = {"default_river_water_flowing.png"},
+ waving = 3,
+ tiles = {"default_river_water_flowing.png"..WATER_ALPHA},
special_tiles = {
- {name = "default_river_water_flowing.png", backface_culling = false},
- {name = "default_river_water_flowing.png", backface_culling = false},
+ {name = "default_river_water_flowing.png"..WATER_ALPHA,
+ backface_culling = false},
+ {name = "default_river_water_flowing.png"..WATER_ALPHA,
+ backface_culling = false},
},
- alpha = WATER_ALPHA,
+ use_texture_alpha = "blend",
paramtype = "light",
paramtype2 = "flowingliquid",
walkable = false,
@@ -226,7 +238,9 @@ minetest.register_node("basenodes:river_water_flowing", {
})
minetest.register_node("basenodes:lava_flowing", {
- description = "Flowing Lava",
+ description = "Flowing Lava".."\n"..
+ "4 damage per second".."\n"..
+ "Drowning damage: 1",
drawtype = "flowingliquid",
tiles = {"default_lava_flowing.png"},
special_tiles = {
@@ -251,7 +265,9 @@ minetest.register_node("basenodes:lava_flowing", {
})
minetest.register_node("basenodes:lava_source", {
- description = "Lava Source",
+ description = "Lava Source".."\n"..
+ "4 damage per second".."\n"..
+ "Drowning damage: 1",
drawtype = "liquid",
tiles = { "default_lava.png" },
special_tiles = {
@@ -290,7 +306,8 @@ minetest.register_node("basenodes:mossycobble", {
})
minetest.register_node("basenodes:apple", {
- description = "Apple",
+ description = "Apple".."\n"..
+ "Food (+2)",
drawtype = "plantlike",
tiles ={"default_apple.png"},
inventory_image = "default_apple.png",
diff --git a/games/devtest/mods/basenodes/textures/default_dirt.png b/games/devtest/mods/basenodes/textures/default_dirt.png
index 58670305d..aa75bffb6 100644
--- a/games/devtest/mods/basenodes/textures/default_dirt.png
+++ b/games/devtest/mods/basenodes/textures/default_dirt.png
Binary files differ
diff --git a/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass.png b/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass.png
new file mode 100644
index 000000000..29fde6b26
--- /dev/null
+++ b/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass.png
Binary files differ
diff --git a/games/devtest/mods/basenodes/textures/default_grass_side.png b/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass_side.png
index 04770b6f6..04770b6f6 100644
--- a/games/devtest/mods/basenodes/textures/default_grass_side.png
+++ b/games/devtest/mods/basenodes/textures/dirt_with_grass/default_grass_side.png
Binary files differ
diff --git a/games/devtest/mods/basenodes/textures/info.txt b/games/devtest/mods/basenodes/textures/info.txt
new file mode 100644
index 000000000..2d4ef7efa
--- /dev/null
+++ b/games/devtest/mods/basenodes/textures/info.txt
@@ -0,0 +1,7 @@
+
+The dirt_with_grass folder is for testing loading textures from subfolders.
+If it works correctly, the default_grass_side.png file in the folder is used but
+default_grass.png is not overwritten by the file in the folder.
+
+default_dirt.png should be overwritten by the default_dirt.png in the unittests
+mod which depends on basenodes.
diff --git a/games/devtest/mods/basetools/init.lua b/games/devtest/mods/basetools/init.lua
index c5b4cd76c..bd7480030 100644
--- a/games/devtest/mods/basetools/init.lua
+++ b/games/devtest/mods/basetools/init.lua
@@ -6,7 +6,7 @@
Tool types:
-* Hand: basic tool/weapon (just for convenience, not optimized for testing)
+* Hand: basic tool/weapon (special capabilities in creative mode)
* Pickaxe: dig cracky
* Axe: dig choppy
* Shovel: dig crumbly
@@ -24,25 +24,54 @@ Tool materials:
]]
-- The hand
-minetest.register_item(":", {
- type = "none",
- wield_image = "wieldhand.png",
- wield_scale = {x=1,y=1,z=2.5},
- tool_capabilities = {
- full_punch_interval = 1.0,
- max_drop_level = 0,
- groupcaps = {
- crumbly = {times={[3]=1.50}, uses=0, maxlevel=0},
- snappy = {times={[3]=1.50}, uses=0, maxlevel=0},
- oddly_breakable_by_hand = {times={[1]=7.00,[2]=4.00,[3]=2.00}, uses=0, maxlevel=0},
- },
- damage_groups = {fleshy=1},
- }
-})
+if minetest.settings:get_bool("creative_mode") then
+ local digtime = 42
+ local caps = {times = {digtime, digtime, digtime}, uses = 0, maxlevel = 256}
+
+ minetest.register_item(":", {
+ type = "none",
+ wield_image = "wieldhand.png",
+ wield_scale = {x = 1, y = 1, z = 2.5},
+ range = 10,
+ tool_capabilities = {
+ full_punch_interval = 0.5,
+ max_drop_level = 3,
+ groupcaps = {
+ crumbly = caps,
+ cracky = caps,
+ snappy = caps,
+ choppy = caps,
+ oddly_breakable_by_hand = caps,
+ -- dig_immediate group doesn't use value 1. Value 3 is instant dig
+ dig_immediate =
+ {times = {[2] = digtime, [3] = 0}, uses = 0, maxlevel = 256},
+ },
+ damage_groups = {fleshy = 10},
+ }
+ })
+else
+ minetest.register_item(":", {
+ type = "none",
+ wield_image = "wieldhand.png",
+ wield_scale = {x = 1, y = 1, z = 2.5},
+ tool_capabilities = {
+ full_punch_interval = 0.9,
+ max_drop_level = 0,
+ groupcaps = {
+ crumbly = {times = {[2] = 3.00, [3] = 0.70}, uses = 0, maxlevel = 1},
+ snappy = {times = {[3] = 0.40}, uses = 0, maxlevel = 1},
+ oddly_breakable_by_hand =
+ {times = {[1] = 3.50, [2] = 2.00, [3] = 0.70}, uses = 0}
+ },
+ damage_groups = {fleshy = 1},
+ }
+ })
+end
-- Mese Pickaxe: special tool that digs "everything" instantly
minetest.register_tool("basetools:pick_mese", {
- description = "Mese Pickaxe",
+ description = "Mese Pickaxe".."\n"..
+ "Digs diggable nodes instantly",
inventory_image = "basetools_mesepick.png",
tool_capabilities = {
full_punch_interval = 1.0,
@@ -65,7 +94,9 @@ minetest.register_tool("basetools:pick_mese", {
-- This should break after only 1 use
minetest.register_tool("basetools:pick_dirt", {
- description = "Dirt Pickaxe",
+ description = "Dirt Pickaxe".."\n"..
+ "Digs cracky=3".."\n"..
+ "1 use only",
inventory_image = "basetools_dirtpick.png",
tool_capabilities = {
max_drop_level=0,
@@ -76,7 +107,8 @@ minetest.register_tool("basetools:pick_dirt", {
})
minetest.register_tool("basetools:pick_wood", {
- description = "Wooden Pickaxe",
+ description = "Wooden Pickaxe".."\n"..
+ "Digs cracky=3",
inventory_image = "basetools_woodpick.png",
tool_capabilities = {
max_drop_level=0,
@@ -86,7 +118,8 @@ minetest.register_tool("basetools:pick_wood", {
},
})
minetest.register_tool("basetools:pick_stone", {
- description = "Stone Pickaxe",
+ description = "Stone Pickaxe".."\n"..
+ "Digs cracky=2..3",
inventory_image = "basetools_stonepick.png",
tool_capabilities = {
max_drop_level=0,
@@ -96,7 +129,8 @@ minetest.register_tool("basetools:pick_stone", {
},
})
minetest.register_tool("basetools:pick_steel", {
- description = "Steel Pickaxe",
+ description = "Steel Pickaxe".."\n"..
+ "Digs cracky=1..3",
inventory_image = "basetools_steelpick.png",
tool_capabilities = {
max_drop_level=1,
@@ -106,7 +140,9 @@ minetest.register_tool("basetools:pick_steel", {
},
})
minetest.register_tool("basetools:pick_steel_l1", {
- description = "Steel Pickaxe Level 1",
+ description = "Steel Pickaxe Level 1".."\n"..
+ "Digs cracky=1..3".."\n"..
+ "maxlevel=1",
inventory_image = "basetools_steelpick_l1.png",
tool_capabilities = {
max_drop_level=1,
@@ -116,7 +152,9 @@ minetest.register_tool("basetools:pick_steel_l1", {
},
})
minetest.register_tool("basetools:pick_steel_l2", {
- description = "Steel Pickaxe Level 2",
+ description = "Steel Pickaxe Level 2".."\n"..
+ "Digs cracky=1..3".."\n"..
+ "maxlevel=2",
inventory_image = "basetools_steelpick_l2.png",
tool_capabilities = {
max_drop_level=1,
@@ -131,7 +169,8 @@ minetest.register_tool("basetools:pick_steel_l2", {
--
minetest.register_tool("basetools:shovel_wood", {
- description = "Wooden Shovel",
+ description = "Wooden Shovel".."\n"..
+ "Digs crumbly=3",
inventory_image = "basetools_woodshovel.png",
tool_capabilities = {
max_drop_level=0,
@@ -141,7 +180,8 @@ minetest.register_tool("basetools:shovel_wood", {
},
})
minetest.register_tool("basetools:shovel_stone", {
- description = "Stone Shovel",
+ description = "Stone Shovel".."\n"..
+ "Digs crumbly=2..3",
inventory_image = "basetools_stoneshovel.png",
tool_capabilities = {
max_drop_level=0,
@@ -151,7 +191,8 @@ minetest.register_tool("basetools:shovel_stone", {
},
})
minetest.register_tool("basetools:shovel_steel", {
- description = "Steel Shovel",
+ description = "Steel Shovel".."\n"..
+ "Digs crumbly=1..3",
inventory_image = "basetools_steelshovel.png",
tool_capabilities = {
max_drop_level=1,
@@ -166,7 +207,8 @@ minetest.register_tool("basetools:shovel_steel", {
--
minetest.register_tool("basetools:axe_wood", {
- description = "Wooden Axe",
+ description = "Wooden Axe".."\n"..
+ "Digs choppy=3",
inventory_image = "basetools_woodaxe.png",
tool_capabilities = {
max_drop_level=0,
@@ -176,7 +218,8 @@ minetest.register_tool("basetools:axe_wood", {
},
})
minetest.register_tool("basetools:axe_stone", {
- description = "Stone Axe",
+ description = "Stone Axe".."\n"..
+ "Digs choppy=2..3",
inventory_image = "basetools_stoneaxe.png",
tool_capabilities = {
max_drop_level=0,
@@ -186,7 +229,8 @@ minetest.register_tool("basetools:axe_stone", {
},
})
minetest.register_tool("basetools:axe_steel", {
- description = "Steel Axe",
+ description = "Steel Axe".."\n"..
+ "Digs choppy=1..3",
inventory_image = "basetools_steelaxe.png",
tool_capabilities = {
max_drop_level=1,
@@ -201,7 +245,8 @@ minetest.register_tool("basetools:axe_steel", {
--
minetest.register_tool("basetools:shears_wood", {
- description = "Wooden Shears",
+ description = "Wooden Shears".."\n"..
+ "Digs snappy=3",
inventory_image = "basetools_woodshears.png",
tool_capabilities = {
max_drop_level=0,
@@ -211,7 +256,8 @@ minetest.register_tool("basetools:shears_wood", {
},
})
minetest.register_tool("basetools:shears_stone", {
- description = "Stone Shears",
+ description = "Stone Shears".."\n"..
+ "Digs snappy=2..3",
inventory_image = "basetools_stoneshears.png",
tool_capabilities = {
max_drop_level=0,
@@ -221,7 +267,8 @@ minetest.register_tool("basetools:shears_stone", {
},
})
minetest.register_tool("basetools:shears_steel", {
- description = "Steel Shears",
+ description = "Steel Shears".."\n"..
+ "Digs snappy=1..3",
inventory_image = "basetools_steelshears.png",
tool_capabilities = {
max_drop_level=1,
@@ -236,7 +283,8 @@ minetest.register_tool("basetools:shears_steel", {
--
minetest.register_tool("basetools:sword_wood", {
- description = "Wooden Sword",
+ description = "Wooden Sword".."\n"..
+ "Damage: fleshy=2",
inventory_image = "basetools_woodsword.png",
tool_capabilities = {
full_punch_interval = 1.0,
@@ -244,7 +292,8 @@ minetest.register_tool("basetools:sword_wood", {
}
})
minetest.register_tool("basetools:sword_stone", {
- description = "Stone Sword",
+ description = "Stone Sword".."\n"..
+ "Damage: fleshy=4",
inventory_image = "basetools_stonesword.png",
tool_capabilities = {
full_punch_interval = 1.0,
@@ -253,7 +302,8 @@ minetest.register_tool("basetools:sword_stone", {
}
})
minetest.register_tool("basetools:sword_steel", {
- description = "Steel Sword",
+ description = "Steel Sword".."\n"..
+ "Damage: fleshy=6",
inventory_image = "basetools_steelsword.png",
tool_capabilities = {
full_punch_interval = 1.0,
@@ -264,7 +314,8 @@ minetest.register_tool("basetools:sword_steel", {
-- Fire/Ice sword: Deal damage to non-fleshy damage groups
minetest.register_tool("basetools:sword_fire", {
- description = "Fire Sword",
+ description = "Fire Sword".."\n"..
+ "Damage: icy=6",
inventory_image = "basetools_firesword.png",
tool_capabilities = {
full_punch_interval = 1.0,
@@ -273,12 +324,13 @@ minetest.register_tool("basetools:sword_fire", {
}
})
minetest.register_tool("basetools:sword_ice", {
- description = "Ice Sword",
+ description = "Ice Sword".."\n"..
+ "Damage: fiery=6",
inventory_image = "basetools_icesword.png",
tool_capabilities = {
full_punch_interval = 1.0,
max_drop_level=0,
- damage_groups = {firy=6},
+ damage_groups = {fiery=6},
}
})
@@ -286,7 +338,9 @@ minetest.register_tool("basetools:sword_ice", {
-- Dagger: Low damage, fast punch interval
--
minetest.register_tool("basetools:dagger_steel", {
- description = "Steel Dagger",
+ description = "Steel Dagger".."\n"..
+ "Damage: fleshy=2".."\n"..
+ "Full Punch Interval: 0.5s",
inventory_image = "basetools_steeldagger.png",
tool_capabilities = {
full_punch_interval = 0.5,
diff --git a/games/devtest/mods/bucket/init.lua b/games/devtest/mods/bucket/init.lua
index 3189d4aa6..ff58b0669 100644
--- a/games/devtest/mods/bucket/init.lua
+++ b/games/devtest/mods/bucket/init.lua
@@ -1,7 +1,8 @@
-- Bucket: Punch liquid source or flowing liquid to collect it
minetest.register_tool("bucket:bucket", {
- description = "Bucket",
+ description = "Bucket".."\n"..
+ "Picks up liquid nodes",
inventory_image = "bucket.png",
stack_max = 1,
liquids_pointable = true,
diff --git a/games/devtest/mods/chest/init.lua b/games/devtest/mods/chest/init.lua
index c44522cb9..5798c13e7 100644
--- a/games/devtest/mods/chest/init.lua
+++ b/games/devtest/mods/chest/init.lua
@@ -1,5 +1,6 @@
minetest.register_node("chest:chest", {
- description = "Chest",
+ description = "Chest" .. "\n" ..
+ "32 inventory slots",
tiles ={"chest_chest.png^[sheet:2x2:0,0", "chest_chest.png^[sheet:2x2:0,0",
"chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:1,0",
"chest_chest.png^[sheet:2x2:1,0", "chest_chest.png^[sheet:2x2:0,1"},
@@ -22,6 +23,18 @@ minetest.register_node("chest:chest", {
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
+ allow_metadata_inventory_put = function(pos, listname, index, stack, player)
+ minetest.chat_send_player(player:get_player_name(), "Allow put: " .. stack:to_string())
+ return stack:get_count()
+ end,
+ allow_metadata_inventory_take = function(pos, listname, index, stack, player)
+ minetest.chat_send_player(player:get_player_name(), "Allow take: " .. stack:to_string())
+ return stack:get_count()
+ end,
+ on_metadata_inventory_put = function(pos, listname, index, stack, player)
+ minetest.chat_send_player(player:get_player_name(), "On put: " .. stack:to_string())
+ end,
+ on_metadata_inventory_take = function(pos, listname, index, stack, player)
+ minetest.chat_send_player(player:get_player_name(), "On take: " .. stack:to_string())
+ end,
})
-
-
diff --git a/games/devtest/mods/chest_of_everything/init.lua b/games/devtest/mods/chest_of_everything/init.lua
index 7d61abebf..3e9d2678a 100644
--- a/